From 9c9f50c03a7d8c62906fa61ad33a4c3b832ec568 Mon Sep 17 00:00:00 2001 From: Jana Rajakumar Date: Thu, 9 Nov 2017 14:37:12 -0500 Subject: [PATCH 01/20] Deploy Viscoll 0.6.0 to master - New visualizations. - Import-Export feature. - Web accessibility support. --- .gitignore | 46 +- .ruby-gemset | 1 - .ruby-version | 1 - Gemfile.lock | 269 - README.md | 29 +- app/assets/config/manifest.js | 3 - app/assets/javascripts/application.js | 16 - app/assets/javascripts/cable.js | 13 - app/assets/stylesheets/application.scss | 18 - app/controllers/application_controller.rb | 3 - app/helpers/application_helper.rb | 2 - app/views/layouts/application.html.erb | 14 - bin/rake | 4 - config/initializers/assets.rb | 11 - config/initializers/cookies_serializer.rb | 5 - config/initializers/session_store.rb | 3 - config/routes.rb | 3 - lib/tasks/.keep | 0 log/.keep | 0 public/404.html | 67 - public/422.html | 67 - public/500.html | 66 - public/apple-touch-icon-precomposed.png | 0 public/apple-touch-icon.png | 0 public/favicon.ico | 0 public/robots.txt | 5 - tmp/.keep | 0 vendor/assets/javascripts/.keep | 0 vendor/assets/stylesheets/.keep | 0 .rspec => viscoll-api/.rspec | 3 +- Gemfile => viscoll-api/Gemfile | 49 +- viscoll-api/Gemfile.lock | 240 + viscoll-api/Guardfile | 14 + viscoll-api/README.md | 44 + Rakefile => viscoll-api/Rakefile | 0 .../channels/application_cable/channel.rb | 0 .../channels/application_cable/connection.rb | 0 .../app/controllers/application_controller.rb | 12 + .../app/controllers/concerns}/.keep | 0 .../concerns/rails_jwt_auth/warden_helper.rb | 36 + .../controllers/confirmations_controller.rb | 24 + .../app/controllers/export_controller.rb | 41 + .../app/controllers/feedback_controller.rb | 27 + .../app/controllers/filter_controller.rb | 319 + .../app/controllers/groups_controller.rb | 199 + .../app/controllers/import_controller.rb | 43 + .../app/controllers/leafs_controller.rb | 272 + .../app/controllers/notes_controller.rb | 250 + .../app/controllers/projects_controller.rb | 172 + .../controllers/registrations_controller.rb | 19 + .../app/controllers/sessions_controller.rb | 71 + .../app/controllers/sides_controller.rb | 86 + .../app/controllers/users_controller.rb | 54 + .../controller_helper/export_helper.rb | 199 + .../controller_helper/filter_helper.rb | 99 + .../controller_helper/groups_helper.rb | 22 + .../controller_helper/import_helper.rb | 134 + .../helpers/controller_helper/leafs_helper.rb | 77 + .../controller_helper/projects_helper.rb | 198 + .../group_validation_helper.rb | 121 + .../leaf_validation_helper.rb | 70 + .../project_validation_helper.rb | 50 + .../app}/jobs/application_job.rb | 0 .../app/mailers/account_approval_mailer.rb | 11 + .../app}/mailers/application_mailer.rb | 2 +- viscoll-api/app/mailers/feedback_mailer.rb | 11 + viscoll-api/app/mailers/mailer.rb | 52 + .../app/models/concerns}/.keep | 0 viscoll-api/app/models/group.rb | 77 + viscoll-api/app/models/leaf.rb | 78 + viscoll-api/app/models/note.rb | 49 + viscoll-api/app/models/project.rb | 48 + viscoll-api/app/models/side.rb | 27 + viscoll-api/app/models/user.rb | 12 + .../sendApprovalStatus.html.erb | 12 + .../app/views/exports/show.json.jbuilder | 11 + .../feedback_mailer/sendFeedback.html.erb | 3 + .../app/views/filter/show.json.jbuilder | 11 + .../app}/views/layouts/mailer.html.erb | 0 .../app}/views/layouts/mailer.text.erb | 0 .../app/views/projects/index.json.jbuilder | 3 + .../app/views/projects/show.json.jbuilder | 12 + .../mailer/confirmation_instructions.html.erb | 19 + .../reset_password_instructions.html.erb | 16 + .../mailer/set_password_instructions.html.erb | 13 + .../app/views/sessions/index.json.jbuilder | 13 + .../app/views/users/show.json.jbuilder | 5 + {bin => viscoll-api/bin}/bundle | 0 {bin => viscoll-api/bin}/rails | 5 + viscoll-api/bin/rake | 9 + {bin => viscoll-api/bin}/setup | 0 viscoll-api/bin/spring | 17 + {bin => viscoll-api/bin}/update | 0 config.ru => viscoll-api/config.ru | 0 {config => viscoll-api/config}/application.rb | 23 +- {config => viscoll-api/config}/boot.rb | 0 {config => viscoll-api/config}/cable.yml | 0 {config => viscoll-api/config}/environment.rb | 0 .../config}/environments/development.rb | 10 +- .../config}/environments/production.rb | 16 +- .../config}/environments/test.rb | 1 + .../application_controller_renderer.rb | 0 .../initializers/backtrace_silencers.rb | 0 viscoll-api/config/initializers/cors.rb | 16 + .../initializers/filter_parameter_logging.rb | 0 .../config}/initializers/inflections.rb | 0 .../config}/initializers/mime_types.rb | 0 viscoll-api/config/initializers/mongoid.rb | 11 + .../initializers/new_framework_defaults.rb | 10 +- .../config/initializers/rails_jwt_auth.rb | 37 + .../config}/initializers/wrap_parameters.rb | 0 {config => viscoll-api/config}/locales/en.yml | 0 {config => viscoll-api/config}/mongoid.yml | 18 +- {config => viscoll-api/config}/puma.rb | 0 viscoll-api/config/routes.rb | 51 + {config => viscoll-api/config}/secrets.yml | 6 +- {config => viscoll-api/config}/spring.rb | 0 {db => viscoll-api/db}/seeds.rb | 0 .../concerns => viscoll-api/lib/tasks}/.keep | 0 .../models/concerns => viscoll-api/log}/.keep | 0 viscoll-api/public/docs/index.html | 60 + viscoll-api/public/docs/viscoll_api.yaml | 2910 +++++++ viscoll-api/spec/factories/groups.rb | 24 + viscoll-api/spec/factories/leafs.rb | 7 + viscoll-api/spec/factories/notes.rb | 9 + viscoll-api/spec/factories/projects.rb | 43 + viscoll-api/spec/factories/sides.rb | 5 + viscoll-api/spec/factories/users.rb | 8 + .../controller_helper/export_helper_spec.rb | 15 + .../controller_helper/filter_helper_spec.rb | 15 + .../controller_helper/groups_helper_spec.rb | 15 + .../controller_helper/import_helper_spec.rb | 15 + .../controller_helper/leafs_helper_spec.rb | 15 + .../controller_helper/projects_helper_spec.rb | 15 + .../group_validation_helper_spec.rb | 15 + .../leaf_validation_helper_spec.rb | 15 + .../project_validation_helper_spec.rb | 15 + viscoll-api/spec/mailers/feedback_spec.rb | 22 + .../spec/mailers/previews/feedback_preview.rb | 4 + viscoll-api/spec/models/group_spec.rb | 81 + viscoll-api/spec/models/grouping_spec.rb | 21 + viscoll-api/spec/models/leaf_spec.rb | 43 + viscoll-api/spec/models/note_spec.rb | 53 + viscoll-api/spec/models/side_spec.rb | 14 + {spec => viscoll-api/spec}/rails_helper.rb | 41 +- .../authentication/delete_session_spec.rb | 73 + .../authentication/post_password_spec.rb | 58 + .../authentication/post_registration_spec.rb | 125 + .../authentication/post_session_spec.rb | 84 + .../authentication/put_confirmation_spec.rb | 32 + .../authentication/put_password_spec.rb | 100 + .../requests/feedback/create_feedback_spec.rb | 82 + .../requests/groups/groups_create_spec.rb | 180 + .../groups/groups_destroy_multiple_spec.rb | 151 + .../requests/groups/groups_destroy_spec.rb | 150 + .../groups/groups_update_multiple_spec.rb | 170 + .../requests/groups/groups_update_spec.rb | 156 + .../spec/requests/notes/notes_create_spec.rb | 120 + .../requests/notes/notes_create_type_spec.rb | 129 + .../requests/notes/notes_delete_type_spec.rb | 145 + .../spec/requests/notes/notes_destroy_spec.rb | 124 + .../spec/requests/notes/notes_link_spec.rb | 310 + .../spec/requests/notes/notes_unlink_spec.rb | 307 + .../spec/requests/notes/notes_update_spec.rb | 168 + .../requests/notes/notes_update_type_spec.rb | 172 + .../requests/projects/create_projects_spec.rb | 250 + .../projects/destroy_projects_spec.rb | 143 + .../requests/projects/index_projects_spec.rb | 86 + .../requests/projects/show_projects_spec.rb | 103 + .../requests/projects/update_projects_spec.rb | 174 + .../sides/sides_updateMultiplce_spec.rb | 163 + .../spec/requests/sides/sides_update_spec.rb | 147 + .../users/delete_users_userID_spec.rb | 120 + .../requests/users/get_users_userID_spec.rb | 103 + .../requests/users/put_users_userID_spec.rb | 265 + {spec => viscoll-api/spec}/spec_helper.rb | 16 +- {lib/assets => viscoll-api/tmp}/.keep | 0 viscoll-app/README.md | 38 + .../__test__/actions/projectActions.spec.js | 94 + .../__test__/actions/userActions.spec.js | 163 + .../helpers/MultiSelectAutoComplete.spec.js | 18 + .../structureRelatedReducers.spec.js | 328 + .../__test__/reducers/userReducer.spec.js | 220 + .../__test__/testData/membersStructure01.js | 313 + viscoll-app/assetsTransformer.js | 7 + .../assets/viscoll_component_tree_diagram.svg | 4 + .../docs/assets/viscoll_data_flow_diagram.svg | 4 + viscoll-app/docs/introduction.md | 5 + viscoll-app/package-lock.json | 7670 +++++++++++++++++ viscoll-app/package.json | 71 + viscoll-app/public/favicon.ico | Bin 0 -> 1150 bytes viscoll-app/public/index.html | 40 + viscoll-app/public/manifest.json | 15 + viscoll-app/sass/components/_dialog.scss | 75 + viscoll-app/sass/components/_tooltip.scss | 67 + viscoll-app/sass/index.scss | 21 + viscoll-app/sass/layout/_404.scss | 25 + viscoll-app/sass/layout/_filter.scss | 47 + viscoll-app/sass/layout/_imageManager.scss | 199 + viscoll-app/sass/layout/_infobox.scss | 30 + viscoll-app/sass/layout/_landing.scss | 60 + viscoll-app/sass/layout/_loading.scss | 21 + viscoll-app/sass/layout/_notes.scss | 112 + viscoll-app/sass/layout/_projectPanel.scss | 12 + viscoll-app/sass/layout/_sidebar.scss | 90 + viscoll-app/sass/layout/_tabular.scss | 163 + viscoll-app/sass/layout/_topbar.scss | 25 + viscoll-app/sass/layout/_workspace.scss | 34 + viscoll-app/sass/lib/_mixins.scss | 53 + viscoll-app/sass/lib/_variables.scss | 17 + viscoll-app/sass/typography.scss | 11 + .../editCollation/interactionActions.js | 305 + .../editCollation/modificationActions.js | 428 + viscoll-app/src/actions/projectActions.js | 191 + viscoll-app/src/actions/userActions.js | 152 + viscoll-app/src/assets/blank_page.png | Bin 0 -> 13944 bytes viscoll-app/src/assets/collation.png | Bin 0 -> 55299 bytes viscoll-app/src/assets/logo_white.png | Bin 0 -> 12802 bytes viscoll-app/src/assets/logo_white.svg | 1 + viscoll-app/src/assets/viscoll_loading.gif | Bin 0 -> 27106 bytes .../src/assets/visualMode/PaperGroup.js | 119 + .../src/assets/visualMode/PaperLeaf.js | 439 + .../src/assets/visualMode/PaperManager.js | 604 ++ viscoll-app/src/axiosConfig.js | 49 + .../src/components/authentication/Login.js | 109 + .../src/components/authentication/Login.md | 7 + .../src/components/authentication/Register.js | 124 + .../src/components/authentication/Register.md | 8 + .../authentication/ResendConfirmation.js | 63 + .../authentication/ResetPassword.js | 84 + .../authentication/ResetPassword.md | 9 + .../authentication/ResetPasswordRequest.js | 89 + .../authentication/ResetPasswordRequest.md | 7 + .../collationManager/TabularMode.js | 42 + .../collationManager/ViewingMode.js | 134 + .../components/collationManager/VisualMode.js | 147 + .../collationManager/tabularMode/Group.js | 120 + .../collationManager/tabularMode/Leaf.js | 130 + .../collationManager/tabularMode/Side.js | 83 + .../src/components/dashboard/CloneProject.js | 66 + .../components/dashboard/EditProjectForm.js | 395 + .../components/dashboard/EditProjectForm.md | 9 + .../src/components/dashboard/ImportProject.js | 144 + .../src/components/dashboard/ListView.js | 59 + .../dashboard/NewProjectContainer.js | 281 + .../dashboard/NewProjectSelection.js | 74 + .../components/dashboard/ProjectDetails.js | 56 + .../components/dashboard/ProjectStructure.js | 175 + viscoll-app/src/components/export/Export.js | 65 + .../src/components/filter/FilterRow.js | 205 + .../src/components/global/AppLoadingScreen.js | 31 + .../src/components/global/ImageViewer.js | 92 + .../src/components/global/LoadingScreen.js | 24 + .../src/components/global/Notification.js | 14 + .../src/components/global/PageNotFound.js | 19 + viscoll-app/src/components/global/Panel.js | 33 + .../components/imageManager/AddManifest.js | 90 + .../components/imageManager/DeleteManifest.js | 39 + .../components/imageManager/EditManifest.js | 105 + .../imageManager/ManageManifests.js | 106 + .../src/components/imageManager/MapImages.js | 436 + .../imageManager/mapImages/Constants.js | 4 + .../imageManager/mapImages/ImageBin.js | 63 + .../imageManager/mapImages/ImageItem.js | 82 + .../imageManager/mapImages/SideBin.js | 58 + .../imageManager/mapImages/SideItem.js | 75 + .../src/components/infoBox/GroupInfoBox.js | 532 ++ .../src/components/infoBox/LeafInfoBox.js | 504 ++ .../src/components/infoBox/SideInfoBox.js | 475 + .../infoBox/dialog/AddGroupDialog.js | 510 ++ .../infoBox/dialog/AddGroupDialog.md | 16 + .../infoBox/dialog/AddLeafDialog.js | 389 + .../infoBox/dialog/AddLeafDialog.md | 11 + .../src/components/infoBox/dialog/AddNote.js | 202 + .../dialog/DeleteConfirmationDialog.js | 131 + .../dialog/DeleteConfirmationDialog.md | 5 + .../components/infoBox/dialog/NoteDialog.js | 113 + .../notesManager/DeleteConfirmation.js | 100 + .../components/notesManager/EditNoteForm.js | 465 + .../components/notesManager/ManageNotes.js | 208 + .../components/notesManager/NewNoteForm.js | 197 + .../src/components/notesManager/NoteType.js | 217 + .../components/notesManager/NotesFilter.js | 56 + .../src/components/topbar/UserProfileForm.js | 492 ++ .../src/components/topbar/UserProfileForm.md | 16 + viscoll-app/src/containers/App.js | 66 + viscoll-app/src/containers/Authentication.js | 250 + viscoll-app/src/containers/Authentication.md | 13 + .../src/containers/CollationManager.js | 558 ++ viscoll-app/src/containers/Dashboard.js | 224 + viscoll-app/src/containers/Dashboard.md | 9 + viscoll-app/src/containers/Feedback.js | 133 + viscoll-app/src/containers/Filter.js | 468 + viscoll-app/src/containers/ImageManager.js | 149 + viscoll-app/src/containers/InfoBox.js | 612 ++ viscoll-app/src/containers/InfoBox.md | 6 + viscoll-app/src/containers/NotesManager.js | 271 + viscoll-app/src/containers/Project.js | 88 + viscoll-app/src/containers/Project.md | 6 + viscoll-app/src/containers/TopBar.js | 165 + viscoll-app/src/containers/TopBar.md | 5 + .../src/helpers/MultiSelectAutoComplete.js | 29 + viscoll-app/src/helpers/getLeafsOfGroup.js | 11 + viscoll-app/src/index.js | 13 + .../src/reducers/editCollationReducer.js | 328 + viscoll-app/src/reducers/globalReducer.js | 30 + .../src/reducers/initialStates/active.js | 190 + .../src/reducers/initialStates/global.js | 6 + .../src/reducers/initialStates/projects.js | 6 + .../src/reducers/initialStates/user.js | 12 + viscoll-app/src/reducers/projectReducer.js | 52 + viscoll-app/src/reducers/userReducer.js | 98 + viscoll-app/src/registerServiceWorker.js | 49 + viscoll-app/src/store.js | 39 + viscoll-app/src/styles/App.css | 814 ++ viscoll-app/src/styles/App.css.map | 7 + viscoll-app/src/styles/button.js | 27 + viscoll-app/src/styles/index.css | 5 + viscoll-app/src/styles/infobox.js | 6 + viscoll-app/src/styles/light.js | 41 + viscoll-app/src/styles/sidebar.js | 22 + viscoll-app/src/styles/tabular.js | 32 + viscoll-app/src/styles/textfield.js | 16 + viscoll-app/src/styles/topbar.js | 8 + viscoll-app/styleguide.config.js | 54 + 325 files changed, 37387 insertions(+), 658 deletions(-) delete mode 100644 .ruby-gemset delete mode 100644 .ruby-version delete mode 100644 Gemfile.lock delete mode 100644 app/assets/config/manifest.js delete mode 100644 app/assets/javascripts/application.js delete mode 100644 app/assets/javascripts/cable.js delete mode 100644 app/assets/stylesheets/application.scss delete mode 100644 app/controllers/application_controller.rb delete mode 100644 app/helpers/application_helper.rb delete mode 100644 app/views/layouts/application.html.erb delete mode 100755 bin/rake delete mode 100644 config/initializers/assets.rb delete mode 100644 config/initializers/cookies_serializer.rb delete mode 100644 config/initializers/session_store.rb delete mode 100644 config/routes.rb delete mode 100644 lib/tasks/.keep delete mode 100644 log/.keep delete mode 100644 public/404.html delete mode 100644 public/422.html delete mode 100644 public/500.html delete mode 100644 public/apple-touch-icon-precomposed.png delete mode 100644 public/apple-touch-icon.png delete mode 100644 public/favicon.ico delete mode 100644 public/robots.txt delete mode 100644 tmp/.keep delete mode 100644 vendor/assets/javascripts/.keep delete mode 100644 vendor/assets/stylesheets/.keep rename .rspec => viscoll-api/.rspec (56%) rename Gemfile => viscoll-api/Gemfile (56%) create mode 100644 viscoll-api/Gemfile.lock create mode 100644 viscoll-api/Guardfile create mode 100644 viscoll-api/README.md rename Rakefile => viscoll-api/Rakefile (100%) rename {app => viscoll-api/app}/channels/application_cable/channel.rb (100%) rename {app => viscoll-api/app}/channels/application_cable/connection.rb (100%) create mode 100644 viscoll-api/app/controllers/application_controller.rb rename {app/assets/images => viscoll-api/app/controllers/concerns}/.keep (100%) create mode 100644 viscoll-api/app/controllers/concerns/rails_jwt_auth/warden_helper.rb create mode 100644 viscoll-api/app/controllers/confirmations_controller.rb create mode 100644 viscoll-api/app/controllers/export_controller.rb create mode 100644 viscoll-api/app/controllers/feedback_controller.rb create mode 100644 viscoll-api/app/controllers/filter_controller.rb create mode 100644 viscoll-api/app/controllers/groups_controller.rb create mode 100644 viscoll-api/app/controllers/import_controller.rb create mode 100644 viscoll-api/app/controllers/leafs_controller.rb create mode 100644 viscoll-api/app/controllers/notes_controller.rb create mode 100644 viscoll-api/app/controllers/projects_controller.rb create mode 100644 viscoll-api/app/controllers/registrations_controller.rb create mode 100644 viscoll-api/app/controllers/sessions_controller.rb create mode 100644 viscoll-api/app/controllers/sides_controller.rb create mode 100644 viscoll-api/app/controllers/users_controller.rb create mode 100644 viscoll-api/app/helpers/controller_helper/export_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/filter_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/groups_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/import_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/leafs_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/projects_helper.rb create mode 100644 viscoll-api/app/helpers/validation_helper/group_validation_helper.rb create mode 100644 viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb create mode 100644 viscoll-api/app/helpers/validation_helper/project_validation_helper.rb rename {app => viscoll-api/app}/jobs/application_job.rb (100%) create mode 100644 viscoll-api/app/mailers/account_approval_mailer.rb rename {app => viscoll-api/app}/mailers/application_mailer.rb (57%) create mode 100644 viscoll-api/app/mailers/feedback_mailer.rb create mode 100644 viscoll-api/app/mailers/mailer.rb rename {app/assets/javascripts/channels => viscoll-api/app/models/concerns}/.keep (100%) create mode 100644 viscoll-api/app/models/group.rb create mode 100644 viscoll-api/app/models/leaf.rb create mode 100644 viscoll-api/app/models/note.rb create mode 100644 viscoll-api/app/models/project.rb create mode 100644 viscoll-api/app/models/side.rb create mode 100644 viscoll-api/app/models/user.rb create mode 100644 viscoll-api/app/views/account_approval_mailer/sendApprovalStatus.html.erb create mode 100644 viscoll-api/app/views/exports/show.json.jbuilder create mode 100644 viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb create mode 100644 viscoll-api/app/views/filter/show.json.jbuilder rename {app => viscoll-api/app}/views/layouts/mailer.html.erb (100%) rename {app => viscoll-api/app}/views/layouts/mailer.text.erb (100%) create mode 100644 viscoll-api/app/views/projects/index.json.jbuilder create mode 100644 viscoll-api/app/views/projects/show.json.jbuilder create mode 100644 viscoll-api/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb create mode 100644 viscoll-api/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb create mode 100644 viscoll-api/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb create mode 100644 viscoll-api/app/views/sessions/index.json.jbuilder create mode 100644 viscoll-api/app/views/users/show.json.jbuilder rename {bin => viscoll-api/bin}/bundle (100%) rename {bin => viscoll-api/bin}/rails (53%) create mode 100755 viscoll-api/bin/rake rename {bin => viscoll-api/bin}/setup (100%) create mode 100755 viscoll-api/bin/spring rename {bin => viscoll-api/bin}/update (100%) rename config.ru => viscoll-api/config.ru (100%) rename {config => viscoll-api/config}/application.rb (51%) rename {config => viscoll-api/config}/boot.rb (100%) rename {config => viscoll-api/config}/cable.yml (100%) rename {config => viscoll-api/config}/environment.rb (100%) rename {config => viscoll-api/config}/environments/development.rb (84%) rename {config => viscoll-api/config}/environments/production.rb (88%) rename {config => viscoll-api/config}/environments/test.rb (95%) rename {config => viscoll-api/config}/initializers/application_controller_renderer.rb (100%) rename {config => viscoll-api/config}/initializers/backtrace_silencers.rb (100%) create mode 100644 viscoll-api/config/initializers/cors.rb rename {config => viscoll-api/config}/initializers/filter_parameter_logging.rb (100%) rename {config => viscoll-api/config}/initializers/inflections.rb (100%) rename {config => viscoll-api/config}/initializers/mime_types.rb (100%) create mode 100644 viscoll-api/config/initializers/mongoid.rb rename {config => viscoll-api/config}/initializers/new_framework_defaults.rb (64%) create mode 100644 viscoll-api/config/initializers/rails_jwt_auth.rb rename {config => viscoll-api/config}/initializers/wrap_parameters.rb (100%) rename {config => viscoll-api/config}/locales/en.yml (100%) rename {config => viscoll-api/config}/mongoid.yml (88%) rename {config => viscoll-api/config}/puma.rb (100%) create mode 100644 viscoll-api/config/routes.rb rename {config => viscoll-api/config}/secrets.yml (66%) rename {config => viscoll-api/config}/spring.rb (100%) rename {db => viscoll-api/db}/seeds.rb (100%) rename {app/controllers/concerns => viscoll-api/lib/tasks}/.keep (100%) rename {app/models/concerns => viscoll-api/log}/.keep (100%) create mode 100644 viscoll-api/public/docs/index.html create mode 100644 viscoll-api/public/docs/viscoll_api.yaml create mode 100644 viscoll-api/spec/factories/groups.rb create mode 100644 viscoll-api/spec/factories/leafs.rb create mode 100644 viscoll-api/spec/factories/notes.rb create mode 100644 viscoll-api/spec/factories/projects.rb create mode 100644 viscoll-api/spec/factories/sides.rb create mode 100644 viscoll-api/spec/factories/users.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb create mode 100644 viscoll-api/spec/mailers/feedback_spec.rb create mode 100644 viscoll-api/spec/mailers/previews/feedback_preview.rb create mode 100644 viscoll-api/spec/models/group_spec.rb create mode 100644 viscoll-api/spec/models/grouping_spec.rb create mode 100644 viscoll-api/spec/models/leaf_spec.rb create mode 100644 viscoll-api/spec/models/note_spec.rb create mode 100644 viscoll-api/spec/models/side_spec.rb rename {spec => viscoll-api/spec}/rails_helper.rb (68%) create mode 100644 viscoll-api/spec/requests/authentication/delete_session_spec.rb create mode 100644 viscoll-api/spec/requests/authentication/post_password_spec.rb create mode 100644 viscoll-api/spec/requests/authentication/post_registration_spec.rb create mode 100644 viscoll-api/spec/requests/authentication/post_session_spec.rb create mode 100644 viscoll-api/spec/requests/authentication/put_confirmation_spec.rb create mode 100644 viscoll-api/spec/requests/authentication/put_password_spec.rb create mode 100644 viscoll-api/spec/requests/feedback/create_feedback_spec.rb create mode 100644 viscoll-api/spec/requests/groups/groups_create_spec.rb create mode 100644 viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb create mode 100644 viscoll-api/spec/requests/groups/groups_destroy_spec.rb create mode 100644 viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb create mode 100644 viscoll-api/spec/requests/groups/groups_update_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_create_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_create_type_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_delete_type_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_destroy_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_link_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_unlink_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_update_spec.rb create mode 100644 viscoll-api/spec/requests/notes/notes_update_type_spec.rb create mode 100644 viscoll-api/spec/requests/projects/create_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/destroy_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/index_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/show_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/update_projects_spec.rb create mode 100644 viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb create mode 100644 viscoll-api/spec/requests/sides/sides_update_spec.rb create mode 100644 viscoll-api/spec/requests/users/delete_users_userID_spec.rb create mode 100644 viscoll-api/spec/requests/users/get_users_userID_spec.rb create mode 100644 viscoll-api/spec/requests/users/put_users_userID_spec.rb rename {spec => viscoll-api/spec}/spec_helper.rb (92%) rename {lib/assets => viscoll-api/tmp}/.keep (100%) create mode 100644 viscoll-app/README.md create mode 100644 viscoll-app/__test__/actions/projectActions.spec.js create mode 100644 viscoll-app/__test__/actions/userActions.spec.js create mode 100644 viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js create mode 100644 viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js create mode 100644 viscoll-app/__test__/reducers/userReducer.spec.js create mode 100644 viscoll-app/__test__/testData/membersStructure01.js create mode 100644 viscoll-app/assetsTransformer.js create mode 100644 viscoll-app/docs/assets/viscoll_component_tree_diagram.svg create mode 100644 viscoll-app/docs/assets/viscoll_data_flow_diagram.svg create mode 100644 viscoll-app/docs/introduction.md create mode 100644 viscoll-app/package-lock.json create mode 100644 viscoll-app/package.json create mode 100644 viscoll-app/public/favicon.ico create mode 100644 viscoll-app/public/index.html create mode 100644 viscoll-app/public/manifest.json create mode 100644 viscoll-app/sass/components/_dialog.scss create mode 100644 viscoll-app/sass/components/_tooltip.scss create mode 100644 viscoll-app/sass/index.scss create mode 100644 viscoll-app/sass/layout/_404.scss create mode 100644 viscoll-app/sass/layout/_filter.scss create mode 100644 viscoll-app/sass/layout/_imageManager.scss create mode 100644 viscoll-app/sass/layout/_infobox.scss create mode 100644 viscoll-app/sass/layout/_landing.scss create mode 100644 viscoll-app/sass/layout/_loading.scss create mode 100644 viscoll-app/sass/layout/_notes.scss create mode 100644 viscoll-app/sass/layout/_projectPanel.scss create mode 100644 viscoll-app/sass/layout/_sidebar.scss create mode 100644 viscoll-app/sass/layout/_tabular.scss create mode 100644 viscoll-app/sass/layout/_topbar.scss create mode 100644 viscoll-app/sass/layout/_workspace.scss create mode 100644 viscoll-app/sass/lib/_mixins.scss create mode 100644 viscoll-app/sass/lib/_variables.scss create mode 100644 viscoll-app/sass/typography.scss create mode 100644 viscoll-app/src/actions/editCollation/interactionActions.js create mode 100644 viscoll-app/src/actions/editCollation/modificationActions.js create mode 100644 viscoll-app/src/actions/projectActions.js create mode 100644 viscoll-app/src/actions/userActions.js create mode 100644 viscoll-app/src/assets/blank_page.png create mode 100644 viscoll-app/src/assets/collation.png create mode 100644 viscoll-app/src/assets/logo_white.png create mode 100644 viscoll-app/src/assets/logo_white.svg create mode 100644 viscoll-app/src/assets/viscoll_loading.gif create mode 100644 viscoll-app/src/assets/visualMode/PaperGroup.js create mode 100644 viscoll-app/src/assets/visualMode/PaperLeaf.js create mode 100644 viscoll-app/src/assets/visualMode/PaperManager.js create mode 100644 viscoll-app/src/axiosConfig.js create mode 100644 viscoll-app/src/components/authentication/Login.js create mode 100644 viscoll-app/src/components/authentication/Login.md create mode 100644 viscoll-app/src/components/authentication/Register.js create mode 100644 viscoll-app/src/components/authentication/Register.md create mode 100644 viscoll-app/src/components/authentication/ResendConfirmation.js create mode 100644 viscoll-app/src/components/authentication/ResetPassword.js create mode 100644 viscoll-app/src/components/authentication/ResetPassword.md create mode 100644 viscoll-app/src/components/authentication/ResetPasswordRequest.js create mode 100644 viscoll-app/src/components/authentication/ResetPasswordRequest.md create mode 100644 viscoll-app/src/components/collationManager/TabularMode.js create mode 100644 viscoll-app/src/components/collationManager/ViewingMode.js create mode 100644 viscoll-app/src/components/collationManager/VisualMode.js create mode 100644 viscoll-app/src/components/collationManager/tabularMode/Group.js create mode 100644 viscoll-app/src/components/collationManager/tabularMode/Leaf.js create mode 100644 viscoll-app/src/components/collationManager/tabularMode/Side.js create mode 100644 viscoll-app/src/components/dashboard/CloneProject.js create mode 100644 viscoll-app/src/components/dashboard/EditProjectForm.js create mode 100644 viscoll-app/src/components/dashboard/EditProjectForm.md create mode 100644 viscoll-app/src/components/dashboard/ImportProject.js create mode 100644 viscoll-app/src/components/dashboard/ListView.js create mode 100644 viscoll-app/src/components/dashboard/NewProjectContainer.js create mode 100644 viscoll-app/src/components/dashboard/NewProjectSelection.js create mode 100644 viscoll-app/src/components/dashboard/ProjectDetails.js create mode 100644 viscoll-app/src/components/dashboard/ProjectStructure.js create mode 100644 viscoll-app/src/components/export/Export.js create mode 100644 viscoll-app/src/components/filter/FilterRow.js create mode 100644 viscoll-app/src/components/global/AppLoadingScreen.js create mode 100644 viscoll-app/src/components/global/ImageViewer.js create mode 100644 viscoll-app/src/components/global/LoadingScreen.js create mode 100644 viscoll-app/src/components/global/Notification.js create mode 100644 viscoll-app/src/components/global/PageNotFound.js create mode 100644 viscoll-app/src/components/global/Panel.js create mode 100644 viscoll-app/src/components/imageManager/AddManifest.js create mode 100644 viscoll-app/src/components/imageManager/DeleteManifest.js create mode 100644 viscoll-app/src/components/imageManager/EditManifest.js create mode 100644 viscoll-app/src/components/imageManager/ManageManifests.js create mode 100644 viscoll-app/src/components/imageManager/MapImages.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/Constants.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/ImageBin.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/ImageItem.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/SideBin.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/SideItem.js create mode 100644 viscoll-app/src/components/infoBox/GroupInfoBox.js create mode 100644 viscoll-app/src/components/infoBox/LeafInfoBox.js create mode 100644 viscoll-app/src/components/infoBox/SideInfoBox.js create mode 100644 viscoll-app/src/components/infoBox/dialog/AddGroupDialog.js create mode 100644 viscoll-app/src/components/infoBox/dialog/AddGroupDialog.md create mode 100644 viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js create mode 100644 viscoll-app/src/components/infoBox/dialog/AddLeafDialog.md create mode 100644 viscoll-app/src/components/infoBox/dialog/AddNote.js create mode 100644 viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js create mode 100644 viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.md create mode 100644 viscoll-app/src/components/infoBox/dialog/NoteDialog.js create mode 100644 viscoll-app/src/components/notesManager/DeleteConfirmation.js create mode 100644 viscoll-app/src/components/notesManager/EditNoteForm.js create mode 100644 viscoll-app/src/components/notesManager/ManageNotes.js create mode 100644 viscoll-app/src/components/notesManager/NewNoteForm.js create mode 100644 viscoll-app/src/components/notesManager/NoteType.js create mode 100644 viscoll-app/src/components/notesManager/NotesFilter.js create mode 100644 viscoll-app/src/components/topbar/UserProfileForm.js create mode 100644 viscoll-app/src/components/topbar/UserProfileForm.md create mode 100644 viscoll-app/src/containers/App.js create mode 100644 viscoll-app/src/containers/Authentication.js create mode 100644 viscoll-app/src/containers/Authentication.md create mode 100644 viscoll-app/src/containers/CollationManager.js create mode 100644 viscoll-app/src/containers/Dashboard.js create mode 100644 viscoll-app/src/containers/Dashboard.md create mode 100644 viscoll-app/src/containers/Feedback.js create mode 100644 viscoll-app/src/containers/Filter.js create mode 100644 viscoll-app/src/containers/ImageManager.js create mode 100644 viscoll-app/src/containers/InfoBox.js create mode 100644 viscoll-app/src/containers/InfoBox.md create mode 100644 viscoll-app/src/containers/NotesManager.js create mode 100644 viscoll-app/src/containers/Project.js create mode 100644 viscoll-app/src/containers/Project.md create mode 100644 viscoll-app/src/containers/TopBar.js create mode 100644 viscoll-app/src/containers/TopBar.md create mode 100644 viscoll-app/src/helpers/MultiSelectAutoComplete.js create mode 100644 viscoll-app/src/helpers/getLeafsOfGroup.js create mode 100644 viscoll-app/src/index.js create mode 100644 viscoll-app/src/reducers/editCollationReducer.js create mode 100644 viscoll-app/src/reducers/globalReducer.js create mode 100644 viscoll-app/src/reducers/initialStates/active.js create mode 100644 viscoll-app/src/reducers/initialStates/global.js create mode 100644 viscoll-app/src/reducers/initialStates/projects.js create mode 100644 viscoll-app/src/reducers/initialStates/user.js create mode 100644 viscoll-app/src/reducers/projectReducer.js create mode 100644 viscoll-app/src/reducers/userReducer.js create mode 100644 viscoll-app/src/registerServiceWorker.js create mode 100644 viscoll-app/src/store.js create mode 100644 viscoll-app/src/styles/App.css create mode 100644 viscoll-app/src/styles/App.css.map create mode 100644 viscoll-app/src/styles/button.js create mode 100644 viscoll-app/src/styles/index.css create mode 100644 viscoll-app/src/styles/infobox.js create mode 100644 viscoll-app/src/styles/light.js create mode 100644 viscoll-app/src/styles/sidebar.js create mode 100644 viscoll-app/src/styles/tabular.js create mode 100644 viscoll-app/src/styles/textfield.js create mode 100644 viscoll-app/src/styles/topbar.js create mode 100644 viscoll-app/styleguide.config.js diff --git a/.gitignore b/.gitignore index 48fb168f..978ba3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,52 @@ /.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/ + +# 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 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/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..0dda515e 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,21 @@ -# README +# VisColl -This README would normally document whatever steps are necessary to get the -application up and running. +## Introduction -Things you may want to cover: +VisColl is for building models of the physical collation of manuscripts, and then visualizing them in various ways. The VisColl 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. -* Ruby version +## System Requirements -* System dependencies +- `rvm` (>= 1.29.1) +- `ruby` (>= 2.4.1) +- `node` (>= 6.11.4) +- `npm` (>= 3.10.10) -* Configuration +### Additional Requirements for Development: -* Database creation +- [`mailcatcher`](https://mailcatcher.me/) (>= 0.6.5) +- [Redux DevTools for Firefox or Chrome](https://github.com/zalmoxisus/redux-devtools-extension) (>= 2.15.1) -* Database initialization +## Setup and Testing -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... +Please consult the README files in `viscoll-api` (Rails back-end) and `viscoll-app` (HTML5 front-end) for further instructions. Both need to be running in the final setup. 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/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..a369fca9 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.1', '>= 5.1.1' # 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,18 @@ 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.6' + 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' 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 +44,12 @@ 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' + +# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible +gem 'rack-cors', '~> 0.4.1' + -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 diff --git a/viscoll-api/Gemfile.lock b/viscoll-api/Gemfile.lock new file mode 100644 index 00000000..ca4c3ee7 --- /dev/null +++ b/viscoll-api/Gemfile.lock @@ -0,0 +1,240 @@ +GIT + remote: https://github.com/mongoid-rspec/mongoid-rspec.git + revision: f392d24abc6853895bd959b4916cd82a1b12f6f4 + specs: + mongoid-rspec (4.0.0.pre.alpha1) + activesupport (>= 4.0.0) + mongoid (~> 6.0) + rspec (~> 3.3) + +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.1.1) + actionpack (= 5.1.1) + nio4r (~> 2.0) + websocket-driver (~> 0.6.1) + actionmailer (5.1.1) + actionpack (= 5.1.1) + actionview (= 5.1.1) + activejob (= 5.1.1) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.1.1) + actionview (= 5.1.1) + activesupport (= 5.1.1) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.1.1) + activesupport (= 5.1.1) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.1.1) + activesupport (= 5.1.1) + globalid (>= 0.3.6) + activemodel (5.1.1) + activesupport (= 5.1.1) + activerecord (5.1.1) + activemodel (= 5.1.1) + activesupport (= 5.1.1) + arel (~> 8.0) + activesupport (5.1.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + arel (8.0.0) + bcrypt (3.1.11) + bson (4.2.1) + builder (3.2.3) + byebug (9.0.6) + coderay (1.1.1) + concurrent-ruby (1.0.5) + database_cleaner (1.6.1) + diff-lcs (1.3) + docile (1.1.5) + erubi (1.6.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) + formatador (0.2.5) + globalid (0.4.0) + activesupport (>= 4.2.0) + guard (2.14.1) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (~> 1.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) + i18n (0.8.4) + jbuilder (2.7.0) + activesupport (>= 4.2.0) + multi_json (>= 1.2) + json (2.1.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.0.3) + nokogiri (>= 1.5.9) + lumberjack (1.0.12) + mail (2.6.6) + 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.2.0) + minitest (5.10.2) + mongo (2.4.2) + bson (>= 4.2.1, < 5.0.0) + mongoid (6.2.0) + activemodel (~> 5.1) + mongo (>= 2.4.1, < 3.0.0) + multi_json (1.12.1) + nenv (0.3.0) + nio4r (2.1.0) + nokogiri (1.8.0) + mini_portile2 (~> 2.2.0) + notiffany (0.1.1) + nenv (~> 0.1) + shellany (~> 0.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + puma (3.9.1) + rack (2.0.3) + rack-cors (0.4.1) + rack-test (0.6.3) + rack (>= 1.0) + rails (5.1.1) + actioncable (= 5.1.1) + actionmailer (= 5.1.1) + actionpack (= 5.1.1) + actionview (= 5.1.1) + activejob (= 5.1.1) + activemodel (= 5.1.1) + activerecord (= 5.1.1) + activesupport (= 5.1.1) + bundler (>= 1.3.0, < 2.0) + railties (= 5.1.1) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + rails_jwt_auth (0.16.1) + bcrypt (~> 3.1) + jwt (~> 1.5) + rails (~> 5.0) + warden (~> 1.2) + railties (5.1.1) + actionpack (= 5.1.1) + activesupport (= 5.1.1) + 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.10) + ffi (>= 0.5.0, < 2) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-rails (3.6.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) + rspec_junit_formatter (0.3.0) + rspec-core (>= 2, < 4, != 2.12.0) + shellany (0.0.1) + shoulda-matchers (3.1.1) + activesupport (>= 4.0.0) + simplecov (0.14.1) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.1) + slop (3.6.0) + spring (2.0.2) + 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) + tzinfo (1.2.3) + thread_safe (~> 0.1) + warden (1.2.7) + rack (>= 1.0) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + +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 (~> 0.4.1) + rails (~> 5.1, >= 5.1.1) + rails_jwt_auth (~> 0.16.1) + rspec-rails (~> 3.6) + rspec_junit_formatter (~> 0.3.0) + shoulda-matchers (~> 3.1, >= 3.1.1) + simplecov + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + +BUNDLED WITH + 1.14.6 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..65824374 --- /dev/null +++ b/viscoll-api/README.md @@ -0,0 +1,44 @@ +# 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 +``` + +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..98bc77b2 --- /dev/null +++ b/viscoll-api/app/controllers/application_controller.rb @@ -0,0 +1,12 @@ +class ApplicationController < ActionController::API + include RailsJwtAuth::WardenHelper + include ControllerHelper::ProjectsHelper + include ControllerHelper::GroupsHelper + include ControllerHelper::LeafsHelper + include ControllerHelper::FilterHelper + include ControllerHelper::ImportHelper + 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..8ab80312 --- /dev/null +++ b/viscoll-api/app/controllers/export_controller.rb @@ -0,0 +1,41 @@ +class ExportController < ApplicationController + before_action :authenticate! + before_action :set_project, only: [:show] + + # PUT /projects/id/export/format + def show + begin + case @format + when "xml" + exportData = buildDotModel(@project) + render json: {data: exportData, type: @format}, status: :ok + when "formula" + exportData = buildFormula(@project) + render json: {data: exportData, type: @format}, status: :ok + when "json" + @data = buildJSON(@project) + render :'exports/show', status: :ok + else + render json: {error: "Export format must be one of [json, xml, formula]"}, status: :unprocessable_entity + end + rescue Exception => e + render json: {error: e.message}, status: :internal_server_error + end + end + + private + def set_project + begin + @project = Project.find(params[:id]) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized + return + end + @format = params[:format] + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found + 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..b83ad9ea --- /dev/null +++ b/viscoll-api/app/controllers/feedback_controller.rb @@ -0,0 +1,27 @@ +class FeedbackController < ApplicationController + before_action :authenticate! + + # POST /feedback + def create + begin + @title = feedback_params[:title] + @message = feedback_params[:message] + if not @title or not @message + render json: {error: "[title] and [message] params required."}, status: :unprocessable_entity + return + end + FeedbackMailer.sendFeedback( + @title, + @message, + current_user + ).deliver_now + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + end + end + + private + def feedback_params + params.require(:feedback).permit(:title, :message) + end +end \ No newline at end of file diff --git a/viscoll-api/app/controllers/filter_controller.rb b/viscoll-api/app/controllers/filter_controller.rb new file mode 100644 index 00000000..9ea1a833 --- /dev/null +++ b/viscoll-api/app/controllers/filter_controller.rb @@ -0,0 +1,319 @@ +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 + 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 + end + end + + + + def performFilter(queries) + sets = [] + conjunctions = [] + queries.each do |query| + type = query[:type] + attribute = query[:attribute] + condition = query[:condition] + values = query[:values] + conjunction = query[:conjunction] + groups = [] + leafs = [] + sides = [] + notes = [] + case type + when "group" + case condition + when "equals" + if values.length > 1 + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$in": values}) + else + groupQueryResult = @project.groups.only(:id).where("#{attribute}": values[0]) + end + when "not equals" + if values.length > 1 + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$nin": values}) + else + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$ne": values[0]}) + end + when "contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$in": values}) + else + groupQueryResult = @project.groups.only(:id).where("#{attribute}": /#{values[0]}/) + end + when "not contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$nin": values}) + else + groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$not": /#{values[0]}/}) + end + end + groupQueryResult.each do |leafID| + groups.push(leafID.id.to_s) + end + @objectIDs[:Groups] = @objectIDs[:Groups] + groups + if groups.length > 0 + @visibleAttributes[:group]["#{attribute}"] = true + end + when "leaf" + if attribute == "conjoined_leaf_order" + old_attribute = attribute + attribute = "conjoined_to" + end + case condition + when "equals" + if values.length > 1 + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$in": values}) + else + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": values[0]) + end + when "not equals" + if values.length > 1 + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$nin": values}) + else + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$ne": values[0]}) + end + when "contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$in": values}) + else + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": /#{values[0]}/) + end + when "not contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$nin": values}) + else + leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$not": /#{values[0]}/}) + end + end + leafQueryResult.each do |leafID| + leafs.push(leafID.id.to_s) + end + if leafs.length > 0 + if old_attribute + @visibleAttributes[:leaf]["#{old_attribute}"] = true + else + @visibleAttributes[:leaf]["#{attribute}"] = true + end + end + @objectIDs[:Leafs] = @objectIDs[:Leafs] + leafs + when "side" + @project.sides.each do |side| + sides.push(side.id.to_s) + end + case condition + when "equals" + if values.length > 1 + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$in": values}) + else + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": values[0]) + end + when "not equals" + if values.length > 1 + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$nin": values}) + else + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$ne": values[0]}) + end + when "contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$in": values}) + else + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": /#{values[0]}/) + end + when "not contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$nin": values}) + else + sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$not": /#{values[0]}/}) + end + end + sides = [] + sideQueryResult.each do |sideID| + sides.push(sideID.id.to_s) + end + if sides.length > 0 + @visibleAttributes[:side]["#{attribute}"] = true + end + @objectIDs[:Sides] = @objectIDs[:Sides] + sides + when "note" + case condition + when "equals" + if values.length > 1 + noteQueryResult = Note.where("#{attribute}": {"$in": values}) + else + noteQueryResult = Note.where("#{attribute}": values[0]) + end + when "not equals" + if values.length > 1 + noteQueryResult = Note.where("#{attribute}": {"$nin": values}) + else + noteQueryResult = Note.where("#{attribute}": {"$ne": values[0]}) + end + when "contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + noteQueryResult = Note.where("#{attribute}": {"$in": values}) + else + noteQueryResult = Note.where("#{attribute}": /#{values[0]}/) + end + when "not contains" + if values.length > 1 + values = values.map {|x| /^#{x}/} + noteQueryResult = Note.where("#{attribute}": {"$nin": values}) + else + noteQueryResult = Note.where("#{attribute}": {"$not": /#{values[0]}/}) + end + end + noteQueryResult.each do |noteID| + notes.push(noteID.id.to_s) + end + @objectIDs[:Notes] = @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 + return + end + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found + 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..f10ed902 --- /dev/null +++ b/viscoll-api/app/controllers/groups_controller.rb @@ -0,0 +1,199 @@ +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] + project_id = group_params.to_h[:project_id] + order = additional_params.to_h[:order] + # 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 + return + end + @groupErrors = {project_id: []} + if (project_id == nil) + @groupErrors[:project_id].push("not found") + render json: {group: @groupErrors}, status: :unprocessable_entity + 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 + return + end + new_groups = [] + new_group_ids = [] + 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 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 + 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 do |group| + if noOfLeafs + addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) + end + end + @project = Project.find(project_id) + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + end + end + + # PATCH/PUT /groups/1 + def update + begin + if @group.update(group_params) + @data = generateResponse() + render :'projects/show', status: :ok + else + render json: @group.errors, status: :unprocessable_entity + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + 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 + return + end + if !@group.update(group_params[:attributes]) + render json: @group.errors, status: :unprocessable_entity + return + end + end + @project = Project.find(@group.project_id) + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + end + end + + # DELETE /groups/1 + def destroy + begin + @group = Group.find(params[:id]) + @group.destroy + @project = Project.find(@group.project_id) + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + group.destroy + rescue Exception => e + next + end + end + @project = Project.find(projectID) + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + rescue Exception => e + render json: {error: "group not found"}, status: :not_found + return + end + end + + def group_params + params.require(:group).permit(:project_id, :type, :title, :tacketed) + end + + def additional_params + params.require(:additional).permit(:order, :noOfGroups, :memberOrder, :parentGroupID, :noOfLeafs, :conjoin, :oddMemberLeftOut) + end + + def group_params_batch_update + params.permit(:groups => [:id, :attributes=>[:type, :title]]) + end + + def group_params_batch_delete + params.permit(:projectID, :groups => []) + 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..5e1faf5f --- /dev/null +++ b/viscoll-api/app/controllers/import_controller.rb @@ -0,0 +1,43 @@ +class ImportController < ApplicationController + before_action :authenticate! + + # POST /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] + begin + # Skip all callbacks + Leaf.skip_callback(:create, :after, :create_sides) + case importFormat + when "json" + handleJSONImport(JSON.parse(importData)) + when "xml" + # handleXMLImport(Hash.from_xml(importData)) + when "formula" + + end + # render json: {error: "RETURING ERROR FOR NOW"}, status: :unprocessable_entity + @projects = current_user.projects.order_by(:updated_at => 'desc') + render :'projects/index', status: :ok + rescue Exception => e + render json: {error: errorMessage}, status: :unprocessable_entity + ensure + # Add all callbacks again + Leaf.set_callback(:create, :after, :create_sides) + end + end + + + + private + # Never trust parameters from the scary Internet, only allow the white list through. + def imported_data + params.permit(:importData, :importFormat) + 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..ae69c356 --- /dev/null +++ b/viscoll-api/app/controllers/leafs_controller.rb @@ -0,0 +1,272 @@ +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] + 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 + 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 + return + end + + newlyAddedLeafIDs = [] + newlyAddedLeafs = [] + noOfLeafs.times do |noOfLeafsIndex| + @leaf = Leaf.new(leaf_params) + @leaf.nestLevel = @group.nestLevel + if @leaf.save + newlyAddedLeafs.push(@leaf) + newlyAddedLeafIDs.push(@leaf.id) + else + render json: {leaf: @leaf.errors}, status: :unprocessable_entity + return + end + end + + # Time to Auto-Conjoin + newlyAddedLeafs = newlyAddedLeafs.reverse + if conjoin + if newlyAddedLeafs.size.odd? + newlyAddedLeafs.delete_at(oddMemberLeftOut-1) + end + newlyAddedLeafs.size.times do |i| + if (newlyAddedLeafs.size/2 == i) + break + else + leafOne = newlyAddedLeafs[i] + leafTwo = newlyAddedLeafs[newlyAddedLeafs.size-i-1] + leafOne.update(conjoined_to: leafTwo.id.to_s) + leafTwo.update(conjoined_to: leafOne.id.to_s) + end + end + end + + # Add leaves to parent group + @group.add_members(newlyAddedLeafIDs, memberOrder) + + # SUCCESS + @project = Project.find(project_id) + @data = generateResponse() + render :'projects/show', status: :ok + 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() + end + @data = generateResponse() + render :'projects/show', status: :ok + else + render json: {leaf: @leaf.errors}, status: :unprocessable_entity + end + end + + + # PATCH/PUT /leafs + def updateMultiple + begin + allLeafs = leaf_params_batch_update.to_h[:leafs] + @project = Project.find(leaf_params_batch_update.to_h[:project_id]) + 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 + end + if !@leaf.update(leaf_params[:attributes]) + render json: {leafs: {attributes: {index: @leaf.errors}}}, status: :unprocessable_entity + return + end + if (leaf_params[:attributes].key?(:attached_below)||leaf_params[:attributes].key?(:attached_above)) + update_attached_to() + end + end + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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) + + # 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 + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + end + end + + + # CONJOIN /leafs.json + def conjoinLeafs + begin + leafIDs = leaf_params_batch_delete.to_h[:leafs] + leaves = [] + # VALIDATION ERRORS + @errors = [] + haveErrors = false + leafIDs.each do |leafID| + begin + leaves.push(Leaf.find(leafID)) + rescue Exception => e + @errors.push("leaf not found with id "+leafID) + haveErrors = true + end + end + if leafIDs.size < 2 + @errors = "Minimum of 2 leaves required to conjoin" + haveErrors = true + end + if haveErrors + render json: {leafs: @errors}, status: :unprocessable_entity + return + end + @project = Project.find(leaves[0].project_id) + autoConjoinLeaves(leaves, leaves.length/2) + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "leaf not found with id "+params[:id]}, status: :not_found + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + end + end + # Never trust parameters from the scary internet, only allow the white list through. + def leaf_params + params.require(:leaf).permit(:project_id, :parentID, :material, :type, :attachment_method, :conjoined_to, :stub, :attached_above, :attached_below) + end + + def additional_params + params.require(:additional).permit(:memberOrder, :noOfLeafs, :conjoin, :oddMemberLeftOut) + end + + def leaf_params_batch_update + params.permit(:project_id, :leafs => [:id, :attributes=>[: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..e6ff5d94 --- /dev/null +++ b/viscoll-api/app/controllers/notes_controller.rb @@ -0,0 +1,250 @@ +class NotesController < ApplicationController + before_action :authenticate! + before_action :set_note, only: [:update, :link, :unlink, :destroy] + + # POST /notes + def create + @note = Note.new(note_create_params) + if @note.save + if not Project.find(@note.project_id).noteTypes.include?(@note.type) + render json: {type: "should be one of " +Project.find(@note.project_id).noteTypes.to_s}, status: :unprocessable_entity + @note.delete + return + end + @project = Project.find(@note.project_id) + @data = generateResponse() + render :'projects/show', status: :ok + else + render json: @note.errors, status: :unprocessable_entity + 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 + return + end + if @note.update(note_update_params) + @data = generateResponse() + render :'projects/show', status: :ok + else + render json: @note.errors, status: :unprocessable_entity + end + end + + # DELETE /notes/1 + def destroy + @note.destroy + @data = generateResponse() + render :'projects/show', status: :ok + 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 "Side" + type = id[0]=="R" ? "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 + return + end + unless authorized + render json: {error: ''}, status: :unauthorized + return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {id: type + " object not found with id "+id}, status: :unprocessable_entity + 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 + return + end + @data = generateResponse() + render :'projects/show', status: :ok + 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 + return + end + unless authorized + render json: {error: ''}, status: :unauthorized + return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {id: type + " object not found with id "+id}, status: :unprocessable_entity + 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 + return + end + @data = generateResponse() + render :'projects/show', status: :ok + end + + + + # POST /notes/type + def createType + type = note_type_params.to_h[:type] + project_id = note_type_params.to_h[:project_id] + begin + @project = Project.find(project_id) + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity + return + end + if @project.noteTypes.include?(type) + render json: {type: type+" type already exists in the project"}, status: :unprocessable_entity + return + else + @project.noteTypes.push(type) + @project.save + end + @data = generateResponse() + render :'projects/show', status: :ok + end + + + # DELETE /notes/type + def deleteType + type = note_type_params.to_h[:type] + project_id = note_type_params.to_h[:project_id] + begin + @project = Project.find(project_id) + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity + return + end + if not @project.noteTypes.include?(type) + render json: {type: type+" type doesn't exist in the project"}, status: :unprocessable_entity + return + else + @project.noteTypes.delete(type) + @project.save + @project.notes.where(type: type).each do |note| + note.update(type: "Unknown") + note.save + end + end + @data = generateResponse() + render :'projects/show', status: :ok + end + + + # PUT /notes/type + def updateType + old_type = note_type_params.to_h[:old_type] + type = note_type_params.to_h[:type] + project_id = note_type_params.to_h[:project_id] + begin + @project = Project.find(project_id) + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity + return + end + if not @project.noteTypes.include?(old_type) + render json: {old_type: old_type+" type doesn't exist in the project"}, status: :unprocessable_entity + return + elsif @project.noteTypes.include?(type) + render json: {type: type+" already exists in the project"}, status: :unprocessable_entity + 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 + @data = generateResponse() + render :'projects/show', status: :ok + 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 + return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "note not found with id "+params[:id]}, status: :not_found + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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, :title, :type, :description) + 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..cdc16fe8 --- /dev/null +++ b/viscoll-api/app/controllers/projects_controller.rb @@ -0,0 +1,172 @@ +class ProjectsController < ApplicationController + + before_action :authenticate! + before_action :set_project, only: [:show, :update, :destroy, :createManifest, :updateManifest, :deleteManifest] + + # GET /projects + def index + @projects = current_user.projects + end + + # GET /projects/1 + def show + begin + @data = generateResponse() + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + return + end + end + + # POST /projects + def create + begin + # Run validatins for groups params + allGroups = group_params.to_h["groups"] + validationResult = validateProjectCreateGroupsParams(allGroups) + if (not validationResult[:status]) + render json: {groups: validationResult[:errors]}, status: :unprocessable_entity + 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) + end + # Get list of all projects of current user to return in response + @projects = current_user.projects.order_by(:updated_at => 'desc') + render :index, status: :ok + else + render json: {project: @project.errors}, status: :unprocessable_entity + end + rescue Exception => e + render json: {errors: e.message}, status: :bad_request + end + end + + # PATCH/PUT /projects/1 + def update + begin + @project = Project.find(params[:id]) + if @project.update(project_params) + @projects = current_user.projects + render :index, status: :ok + else + render json: {project: @project.errors}, status: :unprocessable_entity + end + rescue Exception => e + render json: {errors: e.message}, status: :bad_request + end + end + + # DELETE /projects/1 + def destroy + begin + # Skip some callbacks + Leaf.skip_callback(:destroy, :before, :unlink_notes) + @project.destroy + @projects = current_user.projects + render :index, status: :ok + rescue Exception => e + render json: {errors: e.message}, status: :bad_request + ensure + # Enable callbacks again + Leaf.set_callback(:destroy, :before, :unlink_notes) + end + end + + # POST /projects/:id/manifests + def createManifest + begin + manifest = manifest_params.to_h + manifestID = Project.new.id.to_s + @project.manifests[manifestID] = {id: manifestID, url: manifest[:url]} + @project.save + @data = generateResponse() + render :show, status: :ok + rescue Exception => e + render json: {errors: e}, status: :bad_request + 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 + 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 + return + end + # ONLY UPDATING MANIFEST NAME FOR NOW + @project.manifests[manifest["id"]]["name"] = manifest["name"] + @project.save + @data = generateResponse() + render :show, status: :ok + rescue Exception => e + render json: {errors: e.message}, status: :bad_request + 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 + 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 + @data = generateResponse() + render :show, status: :ok + rescue Exception => e + render json: {errors: e.message}, status: :bad_request + end + end + + private + def set_project + begin + @project = Project.find(params[:id]) + if (@project.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized + return + end + rescue Exception => e + render json: {error: "project not found with id "+params[:id]}, status: :not_found + 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 group_params + params.permit(:groups => [:number, :leaves, :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..e941269c --- /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 + 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..dcecc77e --- /dev/null +++ b/viscoll-api/app/controllers/sides_controller.rb @@ -0,0 +1,86 @@ +class SidesController < ApplicationController + before_action :authenticate! + before_action :set_side, only: [:update] + + # PATCH/PUT /sides/1 + def update + begin + if @side.update(side_params) + @data = generateResponse() + render :'projects/show', status: :ok + else + render json: @side.errors, status: :unprocessable_entity + end + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + if haveErrors + render json: {sides: @errors}, status: :unprocessable_entity + return + end + allSides.each_with_index do |side_params, index| + side = sides[index] + if !side.update(side_params[:attributes]) + render json: side.errors, status: :unprocessable_entity + return + end + end + @data = generateResponse() + render :'projects/show', status: :ok + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + rescue Exception => e + render json: {error: "side not found"}, status: :not_found + 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, :texture, :script_direction, :image=>[:manifestID, :label, :url]) + end + + def side_params_batch_update + params.permit(:sides => [:id, :attributes=>[:texture, :script_direction, :image=>[:manifestID, :label, :url]]]) + 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..649acc27 --- /dev/null +++ b/viscoll-api/app/controllers/users_controller.rb @@ -0,0 +1,54 @@ +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 + else + render json: current_user.errors, status: :unprocessable_entity + 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 + return + end + rescue Exception => e + render json: {error: "user not found with id "+params[:id]}, status: :not_found + 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..7cccbe7c --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/export_helper.rb @@ -0,0 +1,199 @@ +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[group.id.to_s] = { + "order": index + 1, + "type": group.type, + "title": group.title, + "tacketed": group.tacketed, + "nestLevel": group.nestLevel, + "parentID": group.parentID, + "notes": [], + "memberOrders": group.memberIDs, + "memberType": "Group", + "memberOrder": group.parentID ? nil : rootMemberOrder + } + if group.nestLevel == 1 + rootMemberOrder += 1 + end + end + + # Generate @leafIDs + @groups.each do | groupID, group | + if group[:nestLevel] == 1 + getLeafMembers(group[:memberOrders]) + end + end + + @project.leafs.each_with_index do | leaf, index | + @leafs[leaf.id.to_s] = { + "order": index + 1, + "material": leaf.material, + "type": leaf.type, + "attachment_method": leaf.attachment_method, + "conjoined_leaf_order": leaf.conjoined_to ? @leafIDs.index(leaf.conjoined_to) + 1 : nil, + "attached_above": leaf.attached_above, + "attached_below": leaf.attached_below, + "stub": leaf.stub, + "nestLevel": leaf.nestLevel, + "parentOrder": @groups[leaf.parentID][:order], + "rectoOrder": leaf.rectoID, + "versoOrder": leaf.versoID, + "notes": [], + } + end + + @leafIDs.each do | leafID | + leaf = @leafs[leafID] + @rectoIDs.push(leaf[:rectoOrder]) + @versoIDs.push(leaf[:versoOrder]) + end + + # Transform leaf recto and verso IDs to orders + @leafs.each do | leafID, leaf | + leaf[:rectoOrder] = @rectoIDs.index(leaf[:rectoOrder])+1 + leaf[:versoOrder] = @versoIDs.index(leaf[:versoOrder])+1 + end + + + # Transform group.memberOrders to member global order and group.tacketed to leaf order + @groups.each do | groupID, group | + memberOrders = [] + group[:memberOrders].each do |memberID| + if memberID[0] == "G" + memberOrders.push("Group_"+@groups[memberID][:order].to_s) + else + memberOrders.push("Leaf_"+@leafs[memberID][:order].to_s) + end + end + group[:memberOrders] = memberOrders + if group[:tacketed] != "" + group[:tacketed] = @leafs[group[:tacketed]][:order] + end + end + + @project.sides.each_with_index do | side, index | + parentOrder = @leafIDs.index(side.parentID) + 1 + obj = { + "order": index + 1, + "parentOrder": parentOrder, + "folio_number": side.folio_number ? side.folio_number : parentOrder.to_s + side.id[0], + "texture": side.texture, + "image": side.image, + "script_direction": side.script_direction, + } + if side.id[0] == "R" + @rectos[side.id.to_s] = obj + elsif side.id[0] == "V" + @versos[side.id.to_s] = obj + end + end + + @project.notes.each do | note | + @notes[note.id.to_s] = { + "title": note.title, + "type": note.type, + "description": note.description, + "show": note.show, + "objects": {} + } + @notes[note.id.to_s][:objects][:Group] = note.objects["Group"].map { |groupID| @groups[groupID][:order] } + @notes[note.id.to_s][:objects][:Leaf] = note.objects["Leaf"].map { |leafID| @leafs[leafID][:order]} + @notes[note.id.to_s][:objects][:Recto] = note.objects["Recto"].map { |rectoID| @rectos[rectoID][:order]} + @notes[note.id.to_s][:objects][:Verso] = note.objects["Verso"].map { |versoID| @versos[versoID][:order]} + end + + return { + "project": @projectInformation, + "groupIDs": @groupIDs, + "leafIDs": @leafIDs, + "rectoIDs": @rectoIDs, + "versoIDs": @versoIDs, + "groups": @groups, + "leafs": @leafs, + "rectos": @rectos, + "versos": @versos, + "notes": @notes, + } + end + + + # Populate leaf orders recursively + def getLeafMembers(memberIDs) + memberIDs.each_with_index do | memberID, index | + if memberID[0] == "G" + getLeafMembers(@groups[memberID][:memberIDs]) + @groups[memberID][:memberOrder] = index + 1 + elsif memberID[0] == "L" + @leafIDs.push(memberID) + @leafs[memberID] = {"memberOrder": index + 1} + end + end + end + + + def buildDotModel(project) + xml = Nokogiri::XML::Builder.new { |xml| + xml.manuscript do + xml.title project.title + xml.shelfmark project.shelfmark + project.groups.each_with_index do |group| + xml.quire :n => group.order, :level => group.getNestLevel do + leafIndex = 1 + group.get_members.each do |member| + if member[:type]=="Leaf" + leaf = Leaf.find(member[:id]) + n = leafIndex + mode = leaf.type + conjoinedLeaf = project.leafs.find(leaf.conjoined_to) + single = conjoinedLeaf.order < 1 + conjoin = "" + if not single + conjoin = Grouping.find_by(member_id: conjoinedLeaf.id)[:order] + end + position = member[:order] + folio_number = leaf.sides[0].folio_number[/\d+/] + xml.leaf :n => n, :mode => mode, :single => single, :folio_number => folio_number, :conjoin => conjoin, :position => position + leafIndex += 1 + end + end + end + end + end + }.to_xml + return xml + end + + def buildFormula(project) + result = "*4 x1 A-D12 E12 (E7 + 2x1) F-H12 I12 (I3 + 3x1) K-M12 N12 (N5 + 4x1) O-Q12" + return result + end + + + end +end \ No newline at end of file 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..4eaf4432 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/filter_helper.rb @@ -0,0 +1,99 @@ +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: ""} + case query["type"] + when "group" + if !["type", "title"].include?(query["attribute"]) + error["attribute"] = "valid attributes for group: [type, title]" + haveErrors = true + end + case query["attribute"] + when "type" + if !["equals", "not equals"].include?(query["condition"]) + error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals]" + haveErrors = true + end + when "title" + if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) + error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" + haveErrors = true + end + end + when "leaf" + if !["type", "material", "conjoined_leaf_order", "attached_above", "attached_below", "stub"].include?(query["attribute"]) + error["attribute"] = "valid attributes for leaf: [type, material, conjoined_leaf_order, attached_above, attached_below, stub]" + haveErrors = true + end + case query["attribute"] + when "type", "material", "conjoined_to", "attached_to", "stub" + if !["equals", "not equals"].include?(query["condition"]) + error["condition"] = "valid conditions for leaf attribute "+query["attribute"]+" : [equals, not equals]" + haveErrors = true + end + end + when "side" + if !["folio_number", "texture", "script_direction", "uri"].include?(query["attribute"]) + error["attribute"] = "valid attributes for side: [folio_number, texture, script_direction, uri]" + haveErrors = true + end + case query["attribute"] + when "texture", "script_direction" + if !["equals", "not equals"].include?(query["condition"]) + error["condition"] = "valid conditions for side attribute "+query["attribute"]+" : [equals, not equals]" + haveErrors = true + end + when "folio_number", "uri" + if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) + error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" + haveErrors = true + end + end + when "note" + if !["title", "type", "description"].include?(query["attribute"]) + error["attribute"] = "valid attributes for note: [title, type, description]" + haveErrors = true + end + case query["attribute"] + when "type" + if !["equals", "not equals"].include?(query["condition"]) + error["condition"] = "valid conditions for note attribute "+query["attribute"]+" : [equals, not equals]" + haveErrors = true + end + when "title", "description" + if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) + error["condition"] = "valid conditions for note attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" + haveErrors = true + end + end + else + error["type"] = "type should be one of: [group, leaf, side, note]" + haveErrors = true + end + + if queries.length > 1 && index e + end + data["project"]["user_id"] = current_user.id + project = Project.create(data["project"]) + + allLeafsInOrder = [] + # Create all Leafs + data["leafs"].each do |leafID, leafParams| + leafParams["project_id"] = project.id + leaf = Leaf.create(leafParams) + allLeafsInOrder.push(leaf) + end + + allGroupsInOrder = [] + # Create all Groups + data["groups"].each do |groupID, groupParams| + if groupParams["tacketed"] != "" + groupParams["tacketed"] = allLeafsInOrder[groupParams["tacketed"]].id.to_s + end + groupParams["project_id"] = project.id + group = Group.create(groupParams["group"]) + allGroupsInOrder.push(group) + end + + # Update all leafs with correct conjoinedTo leaf IDs + data["leafs"].each do |leafID, leafParams| + if leafParams[:conjoined_to] + leafConjoinedTo = allLeafsInOrder[leafParams[:conjoined_leaf_order]] + leaf.update(conjoined_to: leafConjoinedTo.id.to_s) + end + end + + # Create all Sides + sides = [] + data["sides"].each do |sideParams| + sideParams["side"]["leaf_id"] = project.leafs.find_by(order: sideParams["parentLeafOrder"]).id + side = Side.create(sideParams["side"]) + sides.push(side) + end + + # Create all Notes + data["notes"].each do |noteParams| + noteParams["note"]["project_id"] = project.id + note = Note.create(noteParams["note"]) + # Generate objectIDs of Groups with this note + groupIDs = [] + noteParams["groupOrders"].each do |order| + group = project.groups.find_by(order: order) + group.notes.push(note) + group.save + groupIDs.push(group.id.to_s) + end + leafIDs = [] + noteParams["leafOrders"].each do |order| + leaf = project.leafs.find_by(order: order) + leaf.notes.push(note) + leaf.save + leafIDs.push(leaf.id.to_s) + end + sideIDs = [] + noteParams["sideOrders"].each do |order| + side = sides[order-1] + side.notes.push(note) + side.save + sideIDs.push(side.id.to_s) + end + note.objects["Group"] = groupIDs + note.objects["Leaf"] = leafIDs + note.objects["Side"] = sideIDs + end + + # Create all Groupings + data["groupings"].each do |groupingParams| + group = project.groups.find_by(order: groupingParams["groupOrder"]) + memberOrder = groupingParams["memberOrder"] + if (groupingParams["memberType"]=="Group") + newMember = project.groups.find_by(order: groupingParams["objectOrder"]) + group.add_members([newMember], memberOrder) + elsif (groupingParams["memberType"]=="Leaf") + newMember = project.leafs.find_by(order: groupingParams["objectOrder"]) + group.add_members([newMember], memberOrder) + end + end + end + + + + # XML IMPORT + def handleXMLImport(data) + # Create the Project + # title = data["manuscript"]["title"] + # begin + # Project.find_by(title: title) + # title = "Copy of " + title + " @ " + Time.now.to_s + # rescue Exception => e + # end + # @project = Project.create(title: title, user_id: current_user.id) + + # # Create the Manuscript + # shelfmark = data["manuscript"]["shelfmark"] + # @project = Manuscript.create(shelfmark: shelfmark, project_id: @project.id) + + # # Create None & Binding Leafs + # Leaf.create({project_id: @project.id, order: -1}) + # Leaf.create({project_id: @project.id, order: 0}) + # # Create all Groups + # data["manuscript"]["quire"].each do |quire| + # p quire + # groupOrder = quire["n"] + # nestLevel = quire["level"] + # groupParams["group"]["project_id"] = @project.id + # @group = Group.create(project_id: @project.id, type: "Quire", order: groupOrder) + # # First, create all Leafs in this Group without attributes + # if (quire["leaf"]) + # quire["leaf"].each do |leaf| + # leafOrder = leaf["folio_number"] + # Leaf.create(project_id: @project.id, order: leafOrder) + # end + # end + # end + end + + + end +end \ No newline at end of file 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..900728ad --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb @@ -0,0 +1,77 @@ +module ControllerHelper + module LeafsHelper + + # Auto-Conjoin the given leaves + def autoConjoinLeaves(leaves, oddLeafNumber) + if leaves.size.odd? + oddLeaf = leaves[oddLeafNumber] + if (oddLeaf.conjoined_to) + @project.leaves.find(oddLeaf.conjoined_to).update(conjoined_to: nil) + oddLeaf.update(conjoined_to: nil) + end + leaves.delete_at(oddLeafNumber-1) + end + leaves.each do |leaf| + if 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 + leaves.size.times do |i| + if (leaves.size/2 == i) + break + else + leafOne = leaves[i] + leafTwo = leaves[leaves.size-i-1] + leafOne.update(conjoined_to: leafTwo.id.to_s) + leafTwo.update(conjoined_to: leafOne.id.to_s) + end + 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 + end +end \ No newline at end of file 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..900e4d9c --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/projects_helper.rb @@ -0,0 +1,198 @@ +require 'net/http' +module ControllerHelper + module ProjectsHelper + def addGroupsLeafsConjoin(project, allGroups) + groupIDs = [] + allGroups.each do |groupInfo| + group = Group.new({project_id: project, title:"Default", type:"Quire"}) + + # 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) + end + # Add groups to project + project.add_groupIDs(groupIDs, 0) + end + + + def getManifestInformation(url) + images = [] + 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 + return {name: response["label"][0..150], images: images} + 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 = manifestInformation[:name] + if manifestName.length>50 + manifestName = manifestName[0,47] + "..." + end + @projectInformation[:manifests][manifestID][:images] = manifestInformation[:images] + @projectInformation[:manifests][manifestID][:name] = manifestName + end + + rootMemberOrder = 1 + @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, + "order": index + 1, + "type": group.type, + "title": group.title, + "tacketed": group.tacketed, + "nestLevel": group.nestLevel, + "parentID": group.parentID, + "notes": [], + "memberIDs": group.memberIDs, + "memberType": "Group", + "memberOrder": group.parentID ? nil : rootMemberOrder + } + if group.nestLevel == 1 + rootMemberOrder += 1 + end + end + @groups.each do | group | + if group[1][:nestLevel] == 1 + getLeafMembers(group[1][:memberIDs]) + end + end + @project.leafs.each do | leaf | + @leafs[leaf.id.to_s] = { + "id": leaf.id.to_s, + "order": @leafIDs.index(leaf.id.to_s) + 1, + "material": leaf.material, + "type": leaf.type, + "attachment_method": leaf.attachment_method, + "conjoined_to": leaf.conjoined_to, + "conjoined_leaf_order": leaf.conjoined_to ? @leafIDs.index(leaf.conjoined_to) + 1 : nil, + "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", + "memberOrder": @leafs[leaf.id.to_s][:memberOrder] + } + end + + @leafIDs.each do | leafID | + leaf = @leafs[leafID] + @rectoIDs.push(leaf[:rectoID]) + @versoIDs.push(leaf[:versoID]) + end + + @project.sides.each do | side | + parentOrder = @leafIDs.index(side.parentID) + 1 + obj = { + "id": side.id.to_s, + "parentID": side.parentID, + "parentOrder": parentOrder, + "folio_number": side.folio_number ? side.folio_number : parentOrder.to_s + side.id[0], + "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 + + @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]) + @groups[memberID][:memberOrder] = index + 1 + elsif memberID[0] == "L" + @leafIDs.push(memberID) + @leafs[memberID] = {"memberOrder": index + 1} + end + end + end + + end +end \ No newline at end of file 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..ac3035cf --- /dev/null +++ b/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb @@ -0,0 +1,121 @@ +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 be greater than 0 or less than 999") + haveErrors = true + end + # if parentGroupID != nil + # begin + # Group.find(parentGroupID) + # rescue Exception => e + # haveErrors = true + # additionalErrors[:parentGroupID].push("group not found with id "+parentGroupID) + # end + # 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 be greater than 0 or less than 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 noOfLeafs 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 be greater than 0 and less than noOfLeafs") + haveErrors = true + elsif (noOfLeafs.even?) + additionalErrors[:oddMemberLeftOut].push("should only be 0 if noOfLeafs 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| + begin + Group.find(groupID) + rescue Exception => e + 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] + begin + Group.find(groupID) + rescue Exception => e + 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 \ No newline at end of file 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..72231bb1 --- /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 be greater than 0 or less than 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 noOfLeafs 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 be greater than 0 and less than noOfLeafs") + elsif (noOfLeafs.even?) + additionalErrors[:oddMemberLeftOut].push("should only be 0 if noOfLeafs is even") + end + end + return additionalErrors + end + + end +end \ No newline at end of file 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..7b7dc32b --- /dev/null +++ b/viscoll-api/app/mailers/feedback_mailer.rb @@ -0,0 +1,11 @@ +class FeedbackMailer < ApplicationMailer + def sendFeedback(title, message, current_user) + @title = title + @message = message + @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..b47d16fd --- /dev/null +++ b/viscoll-api/app/mailers/mailer.rb @@ -0,0 +1,52 @@ +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 || "utlviscoll-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..8b195ad5 --- /dev/null +++ b/viscoll-api/app/models/group.rb @@ -0,0 +1,77 @@ +class Group + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :title, type: String, default: "None" + field :type, type: String, default: "None" + field :tacketed, type: String, 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/leaf.rb b/viscoll-api/app/models/leaf.rb new file mode 100644 index 00000000..7e03ea01 --- /dev/null +++ b/viscoll-api/app/models/leaf.rb @@ -0,0 +1,78 @@ +class Leaf + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :material, type: String, default: "None" + field :type, type: String, default: "None" + field :attachment_method, 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 + + + # 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, texture: "Hair"}) + verso = Side.new({parentID: self.id.to_s, project: self.project, texture: "Flesh"}) + 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 +end + diff --git a/viscoll-api/app/models/note.rb b/viscoll-api/app/models/note.rb new file mode 100644 index 00000000..49cc658a --- /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..abd2daad --- /dev/null +++ b/viscoll-api/app/models/project.rb @@ -0,0 +1,48 @@ +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, name: "", url: "", images: [{label: "", 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 + has_many :leafs, dependent: :destroy + has_many :sides, dependent: :destroy + has_many :notes, dependent: :destroy + + # Validations + validates_presence_of :title, :message => "Project title is required." + validates_uniqueness_of :title, :message => "Project title: '%{value}', must be unique.", scope: :user + + before_destroy :destroy_groups + + 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 destroy_groups + self.groups.each do |group| + if group.nestLevel == 1 + group.destroy + end + 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..9c9b99e4 --- /dev/null +++ b/viscoll-api/app/models/side.rb @@ -0,0 +1,27 @@ +class Side + include Mongoid::Document + include Mongoid::Timestamps + + # Fields + field :folio_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 + + protected + # If linked to note(s), remove link from the note(s)'s side + def unlink_notes + self.notes.each do | note | + note.objects[:Side].delete(self.id.to_s) + note.save + 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..6fc9f0aa --- /dev/null +++ b/viscoll-api/app/models/user.rb @@ -0,0 +1,12 @@ +class User + include Mongoid::Document + include RailsJwtAuth::Authenticatable + include RailsJwtAuth::Confirmable + include RailsJwtAuth::Recoverable + include RailsJwtAuth::Trackable + + field :name, type: String, default: "" + + 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..d28ee121 --- /dev/null +++ b/viscoll-api/app/views/exports/show.json.jbuilder @@ -0,0 +1,11 @@ +json.project @data[:project] +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] \ 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..f1f26e08 --- /dev/null +++ b/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb @@ -0,0 +1,3 @@ +

<%= @message %>

+ +

Submitted by: <%= @user.name %> (<%= @user.email %>)

\ 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..69fbb12f --- /dev/null +++ b/viscoll-api/app/views/projects/index.json.jbuilder @@ -0,0 +1,3 @@ +json.array!(@projects.desc(:updated_at)) do | project | + json.extract! project, :id, :title, :shelfmark, :metadata, :created_at, :updated_at +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..fd6e2fd3 --- /dev/null +++ b/viscoll-api/app/views/projects/show.json.jbuilder @@ -0,0 +1,12 @@ +json.merge! @data[:project] + +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] \ 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 51% rename from config/application.rb rename to viscoll-api/config/application.rb index 0b45f014..e0340619 100644 --- a/config/application.rb +++ b/viscoll-api/config/application.rb @@ -9,17 +9,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, :post, :options, :delete, :put] + 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 84% rename from config/environments/development.rb rename to viscoll-api/config/environments/development.rb index 58d20de6..4561e32a 100644 --- a/config/environments/development.rb +++ b/viscoll-api/config/environments/development.rb @@ -30,17 +30,13 @@ 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 => 'localhost', :port => 1025 } # 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 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..7bc1978f --- /dev/null +++ b/viscoll-api/config/initializers/rails_jwt_auth.rb @@ -0,0 +1,37 @@ +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 88% rename from config/mongoid.yml rename to viscoll-api/config/mongoid.yml index b4f74dd1..cdce943a 100644 --- a/config/mongoid.yml +++ b/viscoll-api/config/mongoid.yml @@ -5,7 +5,7 @@ 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: @@ -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,13 +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 + database: viscoll_api_test hosts: - localhost:27017 options: 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..b8be2adf --- /dev/null +++ b/viscoll-api/config/routes.rb @@ -0,0 +1,51 @@ +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}, only: [:show] + get '/projects/:id/export/:format', to: 'export#show', defaults: {format: :json}, only: [:show] + put '/projects/import', to: 'import#index', defaults: {format: :json}, only: [:index] + post '/projects/:id/manifests', to: 'projects#createManifest', defaults: {format: :json}, only: [:create] + put '/projects/:id/manifests', to: 'projects#updateManifest', defaults: {format: :json}, only: [:update] + delete '/projects/:id/manifests', to: 'projects#deleteManifest', defaults: {format: :json}, only: [:destroy] + resources :projects, defaults: {format: :json}, only: [:index, :show, :update, :destroy, :create] + + # 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/: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') + + +end diff --git a/config/secrets.yml b/viscoll-api/config/secrets.yml similarity index 66% rename from config/secrets.yml rename to viscoll-api/config/secrets.yml index 34116365..96ae2160 100644 --- a/config/secrets.yml +++ b/viscoll-api/config/secrets.yml @@ -11,10 +11,10 @@ # if you're sharing your code publicly. development: - secret_key_base: fe3e18f8b5a59698d997c8e5dd20303ed84a86ed28f4a94469adcebf8e2c4905e635761aa743ce87d4b88275ad44c696ee83cc2cd8859f0cdc393c494caf092c - + secret_key_base: 98eb3bcbe406c141ad93c58e5f5ff08ab7348c82d688b78ee1fb1a30559d7104081e0dd9bf97c8e080a54f1d408f7f9d22710439c44cbbddc332861994b1c531 + admin_email: 'smtp://localhost:1025' 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/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/spec/factories/groups.rb b/viscoll-api/spec/factories/groups.rb new file mode 100644 index 00000000..de75a9a1 --- /dev/null +++ b/viscoll-api/spec/factories/groups.rb @@ -0,0 +1,24 @@ +FactoryGirl.define do + sequence :quire_title do |n| + "Quire #{n}" + end + sequence :booklet_title do |n| + "Booklet #{n}" + end + factory :group, class: Group do + transient do + groupOrder nil + end + title { groupOrder ? "Group #{groupOrder}" : generate(:quire_title) } + type "Quire" + end + + factory :quire, class: Group do + title { generate(:quire_title) } + type "Quire" + end + factory :booklet, class: Group do + title { generate(:booklet_title) } + type "Booklet" + end +end diff --git a/viscoll-api/spec/factories/leafs.rb b/viscoll-api/spec/factories/leafs.rb new file mode 100644 index 00000000..64c1f3a9 --- /dev/null +++ b/viscoll-api/spec/factories/leafs.rb @@ -0,0 +1,7 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + factory :leaf do + material "Paper" + type "Original" + end +end diff --git a/viscoll-api/spec/factories/notes.rb b/viscoll-api/spec/factories/notes.rb new file mode 100644 index 00000000..e864c2c0 --- /dev/null +++ b/viscoll-api/spec/factories/notes.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + sequence :note_title do |n| + "Note #{n}" + end + factory :note do + title { generate(:note_title) } + type "Unknown" + end +end \ No newline at end of file diff --git a/viscoll-api/spec/factories/projects.rb b/viscoll-api/spec/factories/projects.rb new file mode 100644 index 00000000..71a8421f --- /dev/null +++ b/viscoll-api/spec/factories/projects.rb @@ -0,0 +1,43 @@ +include ActionDispatch::TestProcess +FactoryGirl.define do + sequence :title do |n| + "Project #{n}" + end + + factory :empty_project, class: Project do + title { generate(:title) } + user_id { FactoryGirl.create(:user) } + end + + factory :project do + title { generate(:title) } + user_id { FactoryGirl.create(:user) } + end +end + + +# include ActionDispatch::TestProcess +# FactoryGirl.define do +# sequence :shelfmark do |n| +# "Cod. UTL #{n}" +# end + +# sequence :uri do |n| +# "http://www.example.org/#{n}" +# end + +# factory :empty_manuscript, class: Manuscript do +# shelfmark { generate :shelfmark } +# uri "" +# date { Date.today } +# end + +# factory :manuscript do +# shelfmark { generate :shelfmark } +# uri { generate :uri } +# date { Date.today } +# after(:create) do |m| +# m.leafs << FactoryGirl.create(:leaf, manuscript: m) +# end +# end +# end \ No newline at end of file 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/helpers/controller_helper/export_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb new file mode 100644 index 00000000..2a381d6d --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::ExportHelper. For example: +# +# describe ControllerHelper::ExportHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::ExportHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..85b8d8ad --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::FilterHelper. For example: +# +# describe ControllerHelper::FilterHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::FilterHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..c5754a6e --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::GroupsHelper. For example: +# +# describe ControllerHelper::GroupsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::GroupsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..5c1299fa --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::ImportHelper. For example: +# +# describe ControllerHelper::ImportHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::ImportHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..508d6b54 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::LeafsHelper. For example: +# +# describe ControllerHelper::LeafsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::LeafsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..e3d0c7f5 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ControllerHelper::ProjectsHelper. For example: +# +# describe ControllerHelper::ProjectsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ControllerHelper::ProjectsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..e777e749 --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ValidationHelper::GroupValidationHelper. For example: +# +# describe ValidationHelper::GroupValidationHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ValidationHelper::GroupValidationHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..dce669c6 --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ValidationHelper::LeafValidationHelper. For example: +# +# describe ValidationHelper::LeafValidationHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ValidationHelper::LeafValidationHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..067c1309 --- /dev/null +++ b/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ValidationHelper::ProjectValidationHelper. For example: +# +# describe ValidationHelper::ProjectValidationHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ValidationHelper::ProjectValidationHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +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..8af03277 --- /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", @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..7cb6ff6e --- /dev/null +++ b/viscoll-api/spec/models/group_spec.rb @@ -0,0 +1,81 @@ +# require 'rails_helper' + +# RSpec.describe Group, type: :model do +# it { is_expected.to be_mongoid_document } +# it { is_expected.to have_field(:order).of_type(Integer) } +# it { is_expected.to have_field(:title).of_type(String) } +# it { is_expected.to have_field(:type).of_type(String) } +# it { is_expected.to belong_to(:project).with_foreign_key(:project_id) } +# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } +# it { is_expected.to have_and_belong_to_many(:groupings).with_foreign_key(:grouping_ids) } + +# before(:each) do +# @project = FactoryGirl.create(:project) +# @group = @project.get_root_groups[0] +# @leaf = FactoryGirl.create(:leaf, project: @project) +# @group.add_members([@leaf], 1) +# @nestedGroup = FactoryGirl.create(:group, project: @project, groupOrder: 2) +# @group.add_members([@nestedGroup], 2) +# end + +# it "can get its members" do +# expect(@group.get_member_objects.size).to eq(2) +# expect(@nestedGroup.get_member_objects.size).to eq(0) +# expect(@group.get_members.size).to eq(2) +# expect(@nestedGroup.get_members.size).to eq(0) +# end + +# it "can get a flattened list of members" do +# # Add a leaf in the nested group +# nestedLeaf = FactoryGirl.create(:leaf, project: @project) +# @nestedGroup.add_members([nestedLeaf], 1) +# # Expect 4 items: itself, leaf, nested group and nested leaf +# expect(@group.get_flattened_nested_members.size).to eq(4) +# end + +# it "can get all members, including special leaves 'none' and 'binding'" do +# # Expect leaf and nested group +# expect(@group.get_members_special.size).to eq(2) +# end + +# it "can delete a member" do +# expect(@group.get_members.size).to eq(2) +# @group.delete_member(@nestedGroup) +# @group.reload +# expect(@group.get_members.size).to eq(1) +# end + +# it "can delete all its members" do +# @group.destroy_members +# expect(@group.get_members.size).to eq(0) +# end + +# it "can get its parent" do +# expect(@nestedGroup.get_parent).not_to be_nil +# expect(@nestedGroup.get_parent_id).not_to be_nil +# expect(@group.get_parent).to be_nil +# expect(@group.get_parent_id).to be_nil +# end + +# it "does not re-add an existing member" do +# expect(@group.add_members([@leaf], 1)).to eq(false) +# end + +# it "updates existing member orders when a new member gets inserted" do +# newLeaf = FactoryGirl.create(:leaf, project: @project) +# expect(@group.add_members([newLeaf], 1)).to eq(true) +# # Verify order of members +# groupings = @group.get_member_groupings +# expect(groupings[0].member).to eq(newLeaf) +# expect(groupings[1].member).to eq(@leaf) +# expect(groupings[2].member).to eq(@nestedGroup) +# end + +# it "if destroyed, it removes parent's association to this group if any" do +# expect(@group.get_members.size).to eq(2) +# @nestedGroup.destroy +# @group.reload +# expect(@group.get_members.size).to eq(1) +# 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/leaf_spec.rb b/viscoll-api/spec/models/leaf_spec.rb new file mode 100644 index 00000000..170165d2 --- /dev/null +++ b/viscoll-api/spec/models/leaf_spec.rb @@ -0,0 +1,43 @@ +# require 'rails_helper' + +# RSpec.describe Leaf, type: :model do +# it { is_expected.to be_mongoid_document } +# it { is_expected.to have_field(:order).of_type(Integer) } +# 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(:attachment_method).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 belong_to(:project).with_foreign_key(:project_id) } +# it { is_expected.to have_many(:sides).with_foreign_key(:leaf_id) } +# it { is_expected.to have_many(:groupings).with_foreign_key(:member_id) } +# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } + +# before(:each) do +# @project = FactoryGirl.create(:project) +# @leaf = FactoryGirl.create(:leaf, project: @project, order: 1) +# @leaf2 = FactoryGirl.create(:leaf, project: @project, order: 2) +# @group = FactoryGirl.create(:quire) +# end + +# it "can tell you what type it is" do +# expect(@leaf.type_of).to eq "Leaf" +# end + +# it "can get its parent" do +# expect(@leaf.get_parent).to eq false +# @group.add_member(@leaf, 1) +# expect(@leaf.get_parent).to eq @group +# end + +# it "deletes its grouping relationships when it gets deleted" do +# @group.add_member(@leaf, 1) +# memberCount = @group.get_members.size +# @leaf.destroy +# @group.reload +# expect(@group.get_members.size).to_not eq(memberCount) +# end + +# end \ No newline at end of file diff --git a/viscoll-api/spec/models/note_spec.rb b/viscoll-api/spec/models/note_spec.rb new file mode 100644 index 00000000..00b6a437 --- /dev/null +++ b/viscoll-api/spec/models/note_spec.rb @@ -0,0 +1,53 @@ +# 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 belong_to(:project).with_foreign_key(:project_id) } + +# it "updates linked objects before it gets deleted" do +# project = FactoryGirl.create(:project) +# note = FactoryGirl.create(:note, project: project) +# group = project.get_root_groups[0] +# group2 = FactoryGirl.create(:group, project: project, groupOrder: 2) +# leaf = project.leafs[0] +# leaf2 = FactoryGirl.create(:leaf, project: project) +# side = leaf.sides[0] +# side2 = leaf.sides[1] + +# # Link note to objects +# group.notes.push(note) +# group2.notes.push(note) +# leaf.notes.push(note) +# leaf2.notes.push(note) +# side.notes.push(note) +# side2.notes.push(note) +# note.objects[:Group].push(group.id.to_s) +# note.objects[:Group].push(group2.id.to_s) +# note.objects[:Leaf].push(leaf.id.to_s) +# note.objects[:Leaf].push(leaf2.id.to_s) +# note.objects[:Side].push(side.id.to_s) +# note.objects[:Side].push(side2.id.to_s) + +# expect(group.notes.size).to eq(1) +# expect(group2.notes.size).to eq(1) +# expect(leaf.notes.size).to eq(1) +# expect(leaf2.notes.size).to eq(1) +# expect(side.notes.size).to eq(1) +# expect(side2.notes.size).to eq(1) +# group2.destroy +# leaf2.destroy +# side2.destroy + +# note.destroy +# group.reload +# leaf.reload +# side.reload +# expect(group.notes.size).to eq(0) +# expect(leaf.notes.size).to eq(0) +# expect(side.notes.size).to eq(0) +# 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..f56d66d6 --- /dev/null +++ b/viscoll-api/spec/models/side_spec.rb @@ -0,0 +1,14 @@ +# require 'rails_helper' + +# RSpec.describe Side, type: :model do +# it { is_expected.to be_mongoid_document } +# it { is_expected.to have_field(:order).of_type(Integer) } +# 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(:uri).of_type(String) } +# it { is_expected.to have_field(:script_direction).of_type(String) } +# it { is_expected.to have_field(:image).of_type(String) } +# it { is_expected.to belong_to(:leaf).with_foreign_key(:leaf_id) } +# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } + +# end \ No newline at end of file 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..e40ba682 --- /dev/null +++ b/viscoll-api/spec/requests/feedback/create_feedback_spec.rb @@ -0,0 +1,82 @@ +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 + 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..fe5c6716 --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_create_spec.rb @@ -0,0 +1,180 @@ +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", + "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'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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'} + @body = JSON.parse(response.body) + @group2.reload + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..3c1cdaf4 --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb @@ -0,0 +1,151 @@ +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 + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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' + 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 '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'} + @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', 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..cb8cfe80 --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_destroy_spec.rb @@ -0,0 +1,150 @@ +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'} + @body = JSON.parse(response.body) + @project.reload + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..a24b64cf --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb @@ -0,0 +1,170 @@ +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 + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..296ed440 --- /dev/null +++ b/viscoll-api/spec/requests/groups/groups_update_spec.rb @@ -0,0 +1,156 @@ +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'} + @body = JSON.parse(response.body) + @group.reload + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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/notes/notes_create_spec.rb b/viscoll-api/spec/requests/notes/notes_create_spec.rb new file mode 100644 index 00000000..bbce74cb --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_create_spec.rb @@ -0,0 +1,120 @@ +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'} + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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 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 + 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..a7d8b7ee --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb @@ -0,0 +1,129 @@ +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(:ok) + 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 + 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..dd4ed283 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb @@ -0,0 +1,145 @@ +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(:ok) + 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 + 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..564bf7f1 --- /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 200' do + expect(response).to have_http_status(:ok) + 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..acbf1de7 --- /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(:ok) + 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(:ok) + 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: "Side" + } + ] + } + 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(:ok) + 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: "Side" + } + ] + } + 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..f53402a7 --- /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(:ok) + 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(:ok) + 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(:ok) + 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..4e3b90aa --- /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 200' do + expect(response).to have_http_status(:ok) + 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..979c3ae4 --- /dev/null +++ b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb @@ -0,0 +1,172 @@ +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(:ok) + 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 + 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_projects_spec.rb b/viscoll-api/spec/requests/projects/create_projects_spec.rb new file mode 100644 index 00000000..da51939e --- /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[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[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/destroy_projects_spec.rb b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb new file mode 100644 index 00000000..cc8e5eca --- /dev/null +++ b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb @@ -0,0 +1,143 @@ +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}) + end + + context 'with correct authorization' do + context 'and standard params' do + before do + delete '/projects/'+@project1.id, params: '', 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.length).to equal 1 + expect(@body[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: '', 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: '', 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: '', 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: '', 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: '', 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: '', 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/index_projects_spec.rb b/viscoll-api/spec/requests/projects/index_projects_spec.rb new file mode 100644 index 00000000..ed9285d4 --- /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.length).to eq 2 + expect(@body[0]['id']).to eq @project2.id.to_str + expect(@body[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..a98da63f --- /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['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_projects_spec.rb b/viscoll-api/spec/requests/projects/update_projects_spec.rb new file mode 100644 index 00000000..e8acddce --- /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[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_updateMultiplce_spec.rb b/viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb new file mode 100644 index 00000000..023ac4e8 --- /dev/null +++ b/viscoll-api/spec/requests/sides/sides_updateMultiplce_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 200' do + expect(response).to have_http_status(:ok) + 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 "Hair" + 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..bd0ee03f --- /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 200' do + expect(response).to have_http_status(:ok) + 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 "Hair" + 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 92% rename from spec/spec_helper.rb rename to viscoll-api/spec/spec_helper.rb index 8f698be4..619b1482 100644 --- a/spec/spec_helper.rb +++ b/viscoll-api/spec/spec_helper.rb @@ -1,3 +1,14 @@ +# Load and launch SimpleCov at the very top +require 'simplecov' +SimpleCov.start do + add_filter '/spec/' + add_filter '/config/' + add_filter '/mailers/' + 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 # 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 +23,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 @@ -76,7 +84,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..a37e4af5 --- /dev/null +++ b/viscoll-app/README.md @@ -0,0 +1,38 @@ +# 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 this to serve the user interface and bring up a browser window: +``` +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 +``` diff --git a/viscoll-app/__test__/actions/projectActions.spec.js b/viscoll-app/__test__/actions/projectActions.spec.js new file mode 100644 index 00000000..9875e7b7 --- /dev/null +++ b/viscoll-app/__test__/actions/projectActions.spec.js @@ -0,0 +1,94 @@ +import { + loadProject, + createProject, + updateProject, + deleteProject, + loadProjects +} from '../../src/actions/projectActions'; + + +describe('>>>A C T I O N --- Test projectActions', () => { + + // it('+++ actionCreator loadProject', () => { + // const projectID = "123456"; + // const loadProjectAction = loadProject(projectID); + // expect(loadProjectAction).toEqual({ + // types: ['SHOW_LOADING','LOAD_PROJECT_SUCCESS','LOAD_PROJECT_FAILED'], + // payload: { + // request : { + // url: `/projects/${projectID}`, + // method: 'get', + // successMessage: "" , + // errorMessage: "Ooops! Something went wrong", + // }, + // } + // }) + // }); + + it('+++ actionCreator createProject', () => { + const project = "new project object"; + const createProjectAction = createProject(project); + expect(createProjectAction).toEqual({ + types: ['SHOW_LOADING','CREATE_PROJECT_SUCCESS','CREATE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects`, + method: 'post', + data: project, + successMessage: "Successfully created the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }) + }); + + it('+++ actionCreator updateProject', () => { + const project = "new project object"; + const projectID = "123456"; + const updateProjectAction = updateProject(projectID, project); + expect(updateProjectAction).toEqual({ + types: ['NO_LOADING','UPDATE_PROJECT_SUCCESS','UPDATE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'put', + data: {project}, + successMessage: "Successfully updated the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }) + }); + + it('+++ actionCreator deleteProject', () => { + const projectID = "123456"; + const deleteProjectAction = deleteProject(projectID); + expect(deleteProjectAction).toEqual({ + types: ['SHOW_LOADING','DELETE_PROJECT_SUCCESS','DELETE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'delete', + successMessage: "Successfully deleted the project" , + errorMessage: "Ooops! Something went wrong" + } + } + }) + }); + + it('+++ actionCreator loadProjects', () => { + const loadProjectsAction = loadProjects(); + expect(loadProjectsAction).toEqual({ + types: ['NO_LOADING','LOAD_PROJECTS_SUCCESS','LOAD_PROJECTS_FAILED'], + payload: { + request : { + url: `/projects`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }) + }); + +}); diff --git a/viscoll-app/__test__/actions/userActions.spec.js b/viscoll-app/__test__/actions/userActions.spec.js new file mode 100644 index 00000000..7beb4e02 --- /dev/null +++ b/viscoll-app/__test__/actions/userActions.spec.js @@ -0,0 +1,163 @@ +import { + login, + register, + confirm, + logout, + resetPasswordRequest, + resetPassword, + updateProfile, + deleteProfile +} from '../../src/actions/userActions'; + + +describe('>>>A C T I O N --- Test userActions', () => { + it('+++ actionCreator login', () => { + const session = { + session: { + email: "user@mail.com", + password: "secret" + } + }; + const loginAction = login(session); + expect(loginAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator register', () => { + const user = { + email: "user@mail.com", + password: "secret", + name: "user" + }; + const registerAction = register(user); + expect(registerAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator confirm', () => { + const confirmation_token = { + confirmation_token: "5951303fc9bf3c7b9a573a3f" + }; + const confirmAction = confirm(confirmation_token); + expect(confirmAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator logout', () => { + const logoutAction = logout(); + expect(logoutAction).toEqual({ + types: ['NO_LOADING','LOGOUT_SUCCESS','LOGOUT_FAILED'], + payload: { + request : { + url: `/session`, + method: 'delete', + successMessage: "You have successfully logged out" , + errorMessage: "Ooops! Something went wrong" + } + } + }) + }); + + it('+++ actionCreator resetPasswordRequest', () => { + const email = "user@mail.com"; + const resetPasswordRequestAction = resetPasswordRequest(email) + expect(resetPasswordRequestAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator resetPassword', () => { + const password = { + psasword: "secret", + password_confirmation: "secret" + }; + const reset_password_token = "5951303fc9bf3c7b9a573a3f"; + const resetPasswordAction = resetPassword(reset_password_token, password); + expect(resetPasswordAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator updateProfile', () => { + const user = {user: {id: "123", name: "batman"}}; + const userID = "123456"; + const updateProfileAction = updateProfile(user, userID); + expect(updateProfileAction).toEqual({ + 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" + } + } + }) + }); + + it('+++ actionCreator deleteProfile', () => { + const userID = "123456"; + const deleteProfileAction = deleteProfile(userID); + expect(deleteProfileAction).toEqual({ + 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" + } + } + }) + }); + +}); diff --git a/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js b/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js new file mode 100644 index 00000000..bddbb774 --- /dev/null +++ b/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js @@ -0,0 +1,18 @@ +import React from 'react'; +import {shallow} from 'enzyme'; +import MultiSelectAutoComplete from '../../src/helpers/MultiSelectAutoComplete'; + + +describe('>>>MULTISELECT_AUTOCOMPLETE --- Shallow Render REACT COMPONENTS',()=>{ + let wrapper; + + beforeEach(()=>{ + wrapper = shallow({}} selectedItems={[]}/>) + }) + + it('+++ render the DUMB component', () => { + expect(wrapper.length).toEqual(1) + }); + + +}); diff --git a/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js b/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js new file mode 100644 index 00000000..b85c7a56 --- /dev/null +++ b/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js @@ -0,0 +1,328 @@ +import editCollationReducer from '../../../src/reducers/editCollationReducer'; +import { initialState } from '../../../src/reducers/initialStates/active'; + + +describe('>>>R E D U C E R --- Test Collation Structure Related Actions',()=>{ + it('+++ reducer for ADD_LEAF(S)_SUCCESS', () => { + let action = { + type: "ADD_LEAF(S)_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for ADD_GROUP(S)_SUCCESS', () => { + let action = { + type: "ADD_GROUP(S)_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_GROUP_SUCCESS', () => { + let action = { + type: "UPDATE_GROUP_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_GROUPS_SUCCESS', () => { + let action = { + type: "UPDATE_GROUPS_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_LEAF_SUCCESS', () => { + let action = { + type: "UPDATE_LEAF_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_LEAFS_SUCCESS', () => { + let action = { + type: "UPDATE_LEAFS_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_SIDE_SUCCESS', () => { + let action = { + type: "UPDATE_SIDE_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for UPDATE_SIDES_SUCCESS', () => { + let action = { + type: "UPDATE_SIDES_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for DELETE_LEAF_SUCCESS', () => { + let action = { + type: "DELETE_LEAF_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for DELETE_LEAFS_SUCCESS', () => { + let action = { + type: "DELETE_LEAFS_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for DELETE_GROUP_SUCCESS', () => { + let action = { + type: "DELETE_GROUP_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for DELETE_GROUPS_SUCCESS', () => { + let action = { + type: "DELETE_GROUPS_SUCCESS", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + + // Failing Actions + it('+++ reducer for UPDATE_GROUP_FAILED', () => { + let action = { + type: "UPDATE_GROUP_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for UPDATE_GROUPS_FAILED', () => { + let action = { + type: "UPDATE_GROUPS_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for UPDATE_SIDE_FAILED', () => { + let action = { + type: "UPDATE_SIDE_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for UPDATE_SIDES_FAILED', () => { + let action = { + type: "UPDATE_SIDES_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for UPDATE_LEAF_FAILED', () => { + let action = { + type: "UPDATE_LEAF_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for UPDATE_LEAFS_FAILED', () => { + let action = { + type: "UPDATE_LEAFS_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for ADD_LEAF(S)_FAILED', () => { + let action = { + type: "ADD_LEAF_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for ADD_GROUP(S)_FAILED', () => { + let action = { + type: "ADD_GROUP_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for DELETE_LEAF_FAILED', () => { + let action = { + type: "DELETE_LEAF_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for DELETE_LEAFS_FAILED', () => { + let action = { + type: "DELETE_LEAFS_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for DELETE_GROUP_FAILED', () => { + let action = { + type: "DELETE_GROUP_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for DELETE_GROUPS_FAILED', () => { + let action = { + type: "DELETE_GROUPS_FAILED", + payload: "the full project structure this leaf belongs to" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); + it('+++ reducer for LOAD_PROJECT_SUCCESS', () => { + let action = { + type: "LOAD_PROJECT_SUCCESS", + payload: "project object" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...state, + project: action.payload, + notes: action.payload.notes, + noteTypes: action.payload.noteTypes + }) + }); + it('+++ reducer for LOAD_PROJECT_FAILED', () => { + let action = { + type: "LOAD_PROJECT_FAILED", + payload: "project object" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState + }) + }); + it('+++ reducer for persist/REHYDRATE', () => { + let action = { + type: "persist/REHYDRATE", + payload: {active: "saved state"} + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + ...action.payload.active + }) + }); + it('+++ reducer for axios error config', () => { + let action = { + type: "LOAD_PROJECT_FAILED", + error: "some error" + }; + let state = editCollationReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + }) + }); +}); diff --git a/viscoll-app/__test__/reducers/userReducer.spec.js b/viscoll-app/__test__/reducers/userReducer.spec.js new file mode 100644 index 00000000..afb551a0 --- /dev/null +++ b/viscoll-app/__test__/reducers/userReducer.spec.js @@ -0,0 +1,220 @@ +import userReducer from '../../src/reducers/userReducer'; +import { initialState } from '../../src/reducers/initialStates/user'; + + +const authenticatedState = { + authenticated: true, + token: "secretToken", + id: "userID", + name: "user", + email: "user@mail.com", + errors: { + login: {errorMessage: ""}, + register: {email: "", password: ""}, + update: {password: "", current_password: "", email: ""}, + confirmation: "", + } +} + + +describe('>>>R E D U C E R --- Test userReducer',()=>{ + it('+++ reducer for LOGIN_SUCCESS', () => { + let state = initialState + state = userReducer( + state, + { + type: "LOGIN_SUCCESS", + payload: { + session: { + jwt: "secretToken", + id: "userID", + name: "user", + email: "user@mail.com", + lastLoggedIn: "2017-07-12T16:44:31.756Z" + } + } + } + ) + expect(state).toEqual({ + ...state, + authenticated: true, + token: "secretToken", + id: "userID", + name: "user", + email: "user@mail.com", + lastLoggedIn: "2017-07-12T16:44:31.756Z" + }) + }); + + it('+++ reducer for LOGIN_FAILED', () => { + let state = initialState + state = userReducer( + state, + { + type: "LOGIN_FAILED", + payload: { + errors: { + session: ["invalid email / password"] + } + } + } + ) + expect(state).toEqual({ + ...state, + errors: { + ...state.errors, + login: { + errorMessage: ["invalid email / password"] + }, + } + }) + }); + + it('+++ reducer for REGISTER_SUCCESS', () => { + let state = initialState + state = userReducer( + state, + { + type: "REGISTER_SUCCESS" + } + ) + expect(state).toEqual({ + ...state, + registerSuccess: true + }) + }); + + it('+++ reducer for REGISTER_FAILED', () => { + let state = initialState + state = userReducer( + state, + { + type: "REGISTER_FAILED", + payload: { + errors: { + email: ["is already taken"], + password: ["can't be blank"] + } + } + } + ) + expect(state).toEqual({ + ...state, + errors: { + ...state.errors, + register: { + email: ["is already taken"], + password: ["can't be blank"] + } + } + }) + }); + + it('+++ reducer for CONFIRM_SUCCESS', () => { + let state = authenticatedState + state = userReducer(state, {type: "CONFIRM_SUCCESS"}) + expect(state).toEqual({...authenticatedState}) + }); + + it('+++ reducer for CONFIRM_FAILED', () => { + let state = authenticatedState + state = userReducer(state, { + type: "CONFIRM_FAILED", + payload: { errors: { confirmation_token: ["is expired"] } }, + }) + expect(state).toEqual({ + ...authenticatedState, + errors: { + ...authenticatedState.errors, + confirmation: "Confirmation token is expired" + } + }) + }); + + it('+++ reducer for RESET_SUCCESS', () => { + let state = authenticatedState + state = userReducer(state, {type: "RESET_SUCCESS"}) + expect(state).toEqual({...authenticatedState}) + }); + + it('+++ reducer for RESET_FAILED', () => { + let state = authenticatedState + state = userReducer(state, {type: "RESET_FAILED"}) + expect(state).toEqual({...authenticatedState}) + }); + + it('+++ reducer for LOGOUT_SUCCESS', () => { + let state = authenticatedState + state = userReducer(state, {type: "LOGOUT_SUCCESS"}) + expect(state).toEqual({...initialState}) + }); + + it('+++ reducer for LOGOUT_FAILED', () => { + let state = authenticatedState + state = userReducer(state, {type: "LOGOUT_FAILED"}) + expect(state).toEqual({...authenticatedState}) + }); + + it('+++ reducer for UPDATE_PROFILE_SUCCESS', () => { + let state = authenticatedState + let action = { + type: "UPDATE_PROFILE_SUCCESS", + payload: {user: {id: 1, name: "batman"}, errors: "errors"} + }; + state = userReducer(state, action) + expect(state).toEqual({ + ...authenticatedState, + errors: initialState.errors, + ...action.payload + }) + }); + + it('+++ reducer for UPDATE_PROFILE_FAILED', () => { + let state = authenticatedState + let action = { + type: "UPDATE_PROFILE_FAILED", + payload: {user: {id: 1, name: "batman"}, errors: "errors"} + }; + state = userReducer(state, action) + expect(state).toEqual({ + ...authenticatedState, + errors: { + ...state.errors, + update: {...state.errors.update, ...action.payload} + } + }) + }); + + it('+++ reducer for persist/REHYDRATE', () => { + let action = { + type: "persist/REHYDRATE", + payload: {user: {id: 1, name: "batman"}, errors: "errors"} + }; + let state = userReducer(initialState, action); + expect(state).toEqual({ + ...initialState, + ...action.payload.user, + errors: initialState.errors + }); + }); + + it('+++ reducer for axios error config', () => { + let action = { + type: "RESET_FAILED", + error: "some error" + }; + let state = userReducer(initialState, action); + expect(state).toEqual(initialState) + }); + + it('+++ reducer for DEFAULT CASE', () => { + let action = { + type: "SOMETHING ELSE" + }; + let state = userReducer(initialState, action); + expect(state).toEqual({ + ...initialState + }) + }); + +}); diff --git a/viscoll-app/__test__/testData/membersStructure01.js b/viscoll-app/__test__/testData/membersStructure01.js new file mode 100644 index 00000000..4aac880e --- /dev/null +++ b/viscoll-app/__test__/testData/membersStructure01.js @@ -0,0 +1,313 @@ +/* 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", + attachment_method: "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", + attachment_method: "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", + attachment_method: "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", + attachment_method: "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", + attachment_method: "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/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/docs/assets/viscoll_component_tree_diagram.svg b/viscoll-app/docs/assets/viscoll_component_tree_diagram.svg new file mode 100644 index 00000000..674b9c68 --- /dev/null +++ b/viscoll-app/docs/assets/viscoll_component_tree_diagram.svg @@ -0,0 +1,4 @@ + + + + diff --git a/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg b/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg new file mode 100644 index 00000000..e3b37af8 --- /dev/null +++ b/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg @@ -0,0 +1,4 @@ + + + + diff --git a/viscoll-app/docs/introduction.md b/viscoll-app/docs/introduction.md new file mode 100644 index 00000000..51c27107 --- /dev/null +++ b/viscoll-app/docs/introduction.md @@ -0,0 +1,5 @@ +## Data flow of Viscoll +![Data flow diagram of Viscoll](viscoll_data_flow_diagram.svg) + +## Component tree +![Component tree diagram of Viscoll](viscoll_component_tree_diagram.svg) \ No newline at end of file diff --git a/viscoll-app/package-lock.json b/viscoll-app/package-lock.json new file mode 100644 index 00000000..03513cd3 --- /dev/null +++ b/viscoll-app/package-lock.json @@ -0,0 +1,7670 @@ +{ + "name": "viscoll-app", + "version": "0.1.0", + "lockfileVersion": 1, + "dependencies": { + "abab": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.3.tgz", + "integrity": "sha1-uB3l9ydOxOdW15fNg08wNkJyTl0=", + "dev": true + }, + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true + }, + "acorn": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", + "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==" + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "acorn-globals": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "dev": true, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "acorn-object-spread": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz", + "integrity": "sha1-SOrQ9KjrFplaF6Dbn/xqyq2kumg=", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "address": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.0.2.tgz", + "integrity": "sha1-SACB6CtYe6MZRZ/vUS9Rb+A9WK8=", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "anser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.1.tgz", + "integrity": "sha1-w2QYY6lizr75Qeoshwbyy08HFr0=", + "dev": true + }, + "ansi-align": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", + "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "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=" + }, + "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": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==" + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true + }, + "aria-query": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz", + "integrity": "sha1-heMVLNjMW6sY2+1hzZxPzlT6ecM=", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=" + }, + "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==" + }, + "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-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true + }, + "array-iterate": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.1.tgz", + "integrity": "sha1-hlv3+K851rCYLGCQKRSsdrwBCPY=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "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.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "asn1.js": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=" + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "ast-types": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.15.tgz", + "integrity": "sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI=" + }, + "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 + }, + "async": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==" + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "attr-accept": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.0.tgz", + "integrity": "sha1-tc01In8WOTWo8d4Q7T66FpQfa+Y=" + }, + "autoprefixer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.0.tgz", + "integrity": "sha1-rkkTrcIh+mylrTpvgDn2pcBrOHc=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "axios": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz", + "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=" + }, + "axobject-query": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", + "dev": true + }, + "babel-code-frame": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "dev": true + }, + "babel-core": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", + "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=", + "dev": true + }, + "babel-eslint": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", + "dev": true + }, + "babel-generator": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", + "dev": true + }, + "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 + }, + "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 + }, + "babel-helper-builder-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.24.1.tgz", + "integrity": "sha1-CteRfjPI11HmRtrKTnfMGTd9LLw=", + "dev": true + }, + "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 + }, + "babel-helper-define-map": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz", + "integrity": "sha1-epdH8ljYlH0y1RX2qhx70CIEoIA=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "babel-helper-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz", + "integrity": "sha1-024i+rEAjXnYhkjjIRaGgShFbOg=", + "dev": true + }, + "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 + }, + "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 + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true + }, + "babel-jest": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", + "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=", + "dev": true + }, + "babel-loader": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.1.tgz", + "integrity": "sha1-uHE0yLEuPkwqlOBUYIW8aAorhIg=", + "dev": true, + "dependencies": { + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "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 + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true + }, + "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 + }, + "babel-plugin-dynamic-import-node": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.0.2.tgz", + "integrity": "sha1-rbW8j0iokxFUA5WunwzD7UsQuy4=", + "dev": true + }, + "babel-plugin-istanbul": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", + "integrity": "sha1-GN3oS/POMp/d8/QQP66SFFbY5Yc=", + "dev": true, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz", + "integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c=", + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz", + "integrity": "sha1-dsKV3DpHQbFmWt/TFnIV3P8ypXY=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "dev": true, + "dependencies": { + "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 + }, + "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=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", + "integrity": "sha1-h11ryb52HFiirj/u5dxIldjH+SE=", + "dev": true + }, + "babel-plugin-transform-react-constant-elements": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-constant-elements/-/babel-plugin-transform-react-constant-elements-6.23.0.tgz", + "integrity": "sha1-LxGb9NLN1F65uqrldAU8YE9hR90=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "babel-plugin-transform-regenerator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", + "integrity": "sha1-uNowWtQ8PJm0hI5P5AN7dw0jxBg=", + "dev": true + }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true + }, + "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 + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "dependencies": { + "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=", + "dependencies": { + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" + } + } + }, + "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-env": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.5.2.tgz", + "integrity": "sha1-zUrpCm6Utwn5c3SzPl+LmDVWre8=", + "dev": true + }, + "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 + }, + "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 + }, + "babel-preset-jest": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", + "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=", + "dev": true + }, + "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 + }, + "babel-preset-react-app": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-3.0.1.tgz", + "integrity": "sha1-i3RMvkf9V8ho5vkTVSzq4mrjGGA=", + "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 + }, + "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 + }, + "babel-register": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", + "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", + "dev": true + }, + "babel-runtime": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", + "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", + "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-template": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", + "dev": true + }, + "babel-traverse": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", + "dev": true + }, + "babel-types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", + "dev": true + }, + "babylon": { + "version": "6.17.4", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "integrity": "sha1-Pot0AriNIsNCPhN6FXeIOxX/hpo=", + "dev": true + }, + "bail": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", + "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base62": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz", + "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" + }, + "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.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true + }, + "big.js": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=" + }, + "binary-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.9.0.tgz", + "integrity": "sha1-ZlBsFs5vTWkopbPNajPKQelB43s=" + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "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==" + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true + }, + "bowser": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.7.2.tgz", + "integrity": "sha1-uUzGklumteB8QhpY5gHORhEmRXI=" + }, + "boxen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", + "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true, + "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.0.6", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", + "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=" + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=" + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=" + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=" + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=" + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=" + }, + "browserslist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.3.0.tgz", + "integrity": "sha512-jDr9Mea+n+FwI+kR0ce7rXCFBoM7hbL80G/th7oPxuNSK4V5J3LPMHB5vykjeI2h7fgSihBbSdoJPmzUC0606Q==", + "dev": true + }, + "bser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", + "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "dev": true + }, + "buble": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/buble/-/buble-0.15.2.tgz", + "integrity": "sha1-VH/EdIP45egXbYKqXrzLGDsC1hM=", + "dev": true, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "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=" + }, + "bytes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.5.0.tgz", + "integrity": "sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true + } + } + }, + "caniuse-db": { + "version": "1.0.30000712", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000712.tgz", + "integrity": "sha1-iXSDlvnXQZ1fon3ztIhy2tv4MYo=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000712", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000712.tgz", + "integrity": "sha1-tHMt7yRZIk8/eMapuhA6v8xwVnA=", + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "dev": true + }, + "case-sensitive-paths-webpack-plugin": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-1.1.4.tgz", + "integrity": "sha1-iq7dVpmobKwrNM9A2bQUV1iXhHI=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "ccount": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", + "integrity": "sha1-U7ai+BW7d7nChx97mnLDol8djok=", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=" + }, + "chain-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz", + "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "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 + } + } + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "character-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", + "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=", + "dev": true + }, + "character-entities-html4": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.1.tgz", + "integrity": "sha1-NZoqSg9+KdPcKsmb2+Ie45Q46lA=", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", + "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8=", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz", + "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw=", + "dev": true + }, + "cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "dev": true, + "dependencies": { + "domhandler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", + "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", + "dev": true + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "dev": true + } + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=" + }, + "ci-info": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.0.0.tgz", + "integrity": "sha1-3FKF8rTiUYIWg2gcOBwziPRuxTQ=", + "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==" + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "clap": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", + "integrity": "sha1-WckP4+E3EEdG/xlGmiemNP9oyFc=", + "dev": true + }, + "classnames": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", + "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=", + "dev": true + }, + "clean-css": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.7.tgz", + "integrity": "sha1-ua6k+FZ5iJzz6ui0A0nsTr390DI=", + "dev": true + }, + "clean-webpack-plugin": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.16.tgz", + "integrity": "sha1-QiqOFQvz1av9PRS/rLBw6A+y4j8=", + "dev": true, + "dependencies": { + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "dev": true + } + } + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true + }, + "cli-width": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", + "dev": true + }, + "clientjs": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/clientjs/-/clientjs-0.1.11.tgz", + "integrity": "sha1-Rm4bE8Ipo8u9hIT5hTHc1lj4EFQ=" + }, + "clipboard-copy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-1.2.0.tgz", + "integrity": "sha1-9qPeZaiiUvqZP8sqTgz+OqS4dp4=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=" + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true + }, + "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=" + }, + "codemirror": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.28.0.tgz", + "integrity": "sha512-E/Z6050shti9v9ivl0dUClVRM4xaH204jsJmEpNYC6KDTlQwAz+5DdhLzn0tjaL/Mp1P0J1uhZokcSP2RFSwlA==", + "dev": true + }, + "collapse-white-space": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", + "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=", + "dev": true + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true + }, + "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": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "common-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-dir/-/common-dir-1.0.1.tgz", + "integrity": "sha1-T9hyCF68XyYtnMI7D/NLPkV2d/A=", + "dev": true + }, + "common-sequence": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-1.0.2.tgz", + "integrity": "sha1-MOB/P49vf5s97oVPILLTnu4Ibeg=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compressible": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.11.tgz", + "integrity": "sha1-FnGKdd4oPtjmBAQWJaIGRYZ5fYo=", + "dev": true + }, + "compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.0.tgz", + "integrity": "sha1-AwyfGY8WQ6BX13anOOki2kNzAS0=", + "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=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true + }, + "configstore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", + "dev": true, + "dependencies": { + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", + "integrity": "sha1-5R0X+PDvDbkKZP20feMFFVbp8Wk=", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "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.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "dev": true + }, + "content-type-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.1.tgz", + "integrity": "sha1-w+VpiMU8ZRJ/tG1AMqOpACRv3JQ=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "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-to-clipboard": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", + "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==" + }, + "copy-webpack-plugin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.0.1.tgz", + "integrity": "sha1-lyjjg7lDFgUNDHRjlY8rhcCqggA=", + "dev": true, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", + "dev": true + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "dev": true + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "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-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true + } + } + }, + "core-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.0.tgz", + "integrity": "sha1-VpwFCRi+ZIazg3VSAorgRmtxcIY=" + }, + "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": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=" + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=" + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=" + }, + "create-react-class": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=" + }, + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true + }, + "crypto-browserify": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==" + }, + "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-in-js-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-1.0.3.tgz", + "integrity": "sha1-msfgL3Y8+F2UAXZmVl7WiltfMhU=" + }, + "css-loader": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", + "integrity": "sha1-IgMlWZ+PAEUtnOtMPKbIpmeYZC0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true + } + } + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "dev": true + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "dependencies": { + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true + }, + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true + }, + "cssom": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", + "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", + "dev": true + }, + "cssstyle": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", + "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=" + }, + "damerau-levenshtein": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", + "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, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true + }, + "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-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "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.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true + }, + "detect-node": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", + "dev": true + }, + "detect-port-alt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.3.tgz", + "integrity": "sha1-pNLwYddXoDTs83xRQmCph1DysTE=", + "dev": true + }, + "diff": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz", + "integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=" + }, + "disposables": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.1.tgz", + "integrity": "sha1-BkcnoltU9QK9griaot+4358bOeM=" + }, + "dnd-core": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.5.4.tgz", + "integrity": "sha512-BcI782MfTm3wCxeIS5c7tAutyTwEIANtuu3W6/xkoJRwiqhRXKX3BbGlycUxxyzMsKdvvoavxgrC3EMPFNYL9A==" + }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "dev": true, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "dom-converter": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", + "dev": true, + "dependencies": { + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "dom-helpers": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.2.1.tgz", + "integrity": "sha1-MgPgf+0he9H0JLAZc1WC/Deyglo=" + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + } + } + }, + "dom-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", + "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=", + "dev": true + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true + }, + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true + }, + "dotenv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true + }, + "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.17", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.17.tgz", + "integrity": "sha1-QcE0V8xxZsXBXnZ65h2GqMrN7l0=", + "dev": true + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=" + }, + "emoji-regex": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=" + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + }, + "enzyme": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-2.9.1.tgz", + "integrity": "sha1-B9XOaRJBJA+4F78sSxjW5TAkDfY=", + "dev": true + }, + "errno": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=" + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=" + }, + "es-abstract": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.0.tgz", + "integrity": "sha512-Cf9/h5MrXtExM20gSS55YFrGKCyPrRBjIVBtVyy8vmlsDfe0NPKMWj65tPLgzyfPuapWxh5whpXCtW4+AW5mRg==", + "dev": true + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true + }, + "es3ify": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz", + "integrity": "sha1-rZ+l3xrjTz8x4SEbWBiy1RB439E=", + "dependencies": { + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", + "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" + } + } + }, + "es5-ext": { + "version": "0.10.26", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.26.tgz", + "integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=" + }, + "es6-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=" + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=" + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "es6-promise": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.0.tgz", + "integrity": "sha1-3aA8qPn4m8WX5omEKSnee6jOvfA=" + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=" + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=" + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=" + }, + "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.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true + } + } + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=" + }, + "eslint": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", + "dev": true, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "eslint-config-react-app": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-1.0.5.tgz", + "integrity": "sha1-mDN1l7wBzCKZH8vdoHRR87RRFxg=", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", + "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", + "dev": true + }, + "eslint-loader": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", + "integrity": "sha1-ULFY3WJy3O+5fphCVIN/gaWALOA=", + "dev": true + }, + "eslint-module-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", + "dev": true + }, + "eslint-plugin-flowtype": { + "version": "2.33.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.33.0.tgz", + "integrity": "sha1-sng4FO0t3PcplTuPZf9zyQyr7ks=", + "dev": true + }, + "eslint-plugin-import": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", + "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", + "dev": true, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz", + "integrity": "sha1-SpOfduwSUBBSiCMzG/lIzFczgLY=", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.0.1.tgz", + "integrity": "sha1-54EH4eVZxuKxd4a7Z8LioBCtDS8=", + "dev": true + }, + "esmangle-evaluator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esmangle-evaluator/-/esmangle-evaluator-1.0.1.tgz", + "integrity": "sha1-Yg2GbvSGGzMR91dm1SqFcrs8YzY=" + }, + "espree": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.0.tgz", + "integrity": "sha1-mDWGJb3QVYYeon4oZ+pyn69GPY0=", + "dev": true + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=" + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "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.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=" + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", + "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=" + }, + "exec-sh": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", + "integrity": "sha1-FPdd4/INKG75MwmbLOUKkDWc7xA=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=" + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=" + }, + "express": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", + "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", + "dev": true, + "dependencies": { + "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.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "external-editor": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", + "integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=", + "dev": true + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=" + }, + "extract-text-webpack-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", + "integrity": "sha1-aTFbiF+Hbb+W04Gfap8cynrr8Vk=", + "dev": true + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "falafel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", + "integrity": "sha1-wY0k71CRF0pJfzGM0ksCaiXN2rQ=", + "dependencies": { + "acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=" + } + } + }, + "fast-deep-equal": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz", + "integrity": "sha1-XG9FmaumszPuM0Li7ZeGcvEAH40=" + }, + "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 + }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", + "dev": true + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true + }, + "fbjs": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz", + "integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=", + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true + }, + "file-loader": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", + "integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true + }, + "filesize": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz", + "integrity": "sha1-UxSeo0YOOy4CSWKlFkiqVyz5gSI=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=" + }, + "filled-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", + "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q=", + "dev": true + }, + "finalhandler": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", + "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", + "dev": true + }, + "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 + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=" + }, + "findup": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz", + "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=", + "dev": true, + "dependencies": { + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", + "dev": true + }, + "commander": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", + "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=", + "dev": true + } + } + }, + "flat-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "dev": true + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "follow-redirects": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.4.tgz", + "integrity": "sha512-Suw6KewLV2hReSyEOeql+UUkBVyiBm3ok1VPrVFRZnQInWpdoZbbiG5i8aJVSjTr0yQ4Ava0Sh6/joCg1Brdqw==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=" + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", + "dev": true + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=", + "dev": true + }, + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", + "dev": true + }, + "function.name-polyfill": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/function.name-polyfill/-/function.name-polyfill-1.0.5.tgz", + "integrity": "sha1-00m7TiSjJPCBIEVe54oEFCsSV7s=", + "dev": true + }, + "function.prototype.name": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.0.3.tgz", + "integrity": "sha512-5EblxZUdioXi2JiMZ9FUbwYj40eQ9MFHyzFLBSPdlRl3SO8l7SLWuAnQ/at/1Wi4hjJwME/C5WpF2ZfAc8nGNw==", + "dev": true + }, + "fuse.js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.0.5.tgz", + "integrity": "sha1-tY2Fh4gCMh3pRGFlSUe5OvEIZyc=" + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "github-slugger": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.1.3.tgz", + "integrity": "sha1-MUpudZoYwrDMV2DVEsy6tUnFSac=", + "dev": true, + "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=" + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true + }, + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "dev": true + }, + "got": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", + "dev": true + }, + "handle-thing": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "dev": true + }, + "handlebars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", + "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "optional": true + } + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true + } + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true + }, + "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 + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=" + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "highlight.js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", + "dev": true + }, + "history": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/history/-/history-4.6.3.tgz", + "integrity": "sha1-bXI6hxLFgda+836MJvSu3G64aWc=" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" + }, + "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 + }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz", + "integrity": "sha1-eb96eF6klf5mFl5zQVPzY/9UN9o=", + "dev": true + }, + "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-minifier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz", + "integrity": "sha512-iKRzQQDuTCsq0Ultbi/mfJJnR0D3AdZKTq966Gsp92xkmAPCV4Xi08qhJ0Dl3ZAWemSgJ7qZK+UsZc0gFqK6wg==", + "dev": true + }, + "html-webpack-plugin": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", + "integrity": "sha1-LnhjtX5f1I/iYzA+L/yTTDBk0Ak=", + "dev": true, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true + } + } + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "dependencies": { + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "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.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true + }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "dev": true + }, + "http-proxy-middleware": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", + "dev": true, + "dependencies": { + "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-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + }, + "hyphenate-style-name": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", + "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "ignore": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", + "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "immutability-helper": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.4.0.tgz", + "integrity": "sha512-rW/L/56ZMo9NStMK85kFrUFFGy4NeJbCdhfrDHIZrFfxYtuwuxD+dT3mWMcdmrNO61hllc60AeGglCRhfZ1dZw==" + }, + "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": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "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 + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "inline-process-browser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", + "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" + }, + "inline-style-prefixer": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz", + "integrity": "sha1-DMyS5ZAv5uDSjZdcQlhEP4gGFfg=" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true + }, + "interpret": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=" + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", + "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-alphabetical": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", + "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", + "dev": true + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", + "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "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=" + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-ci": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", + "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", + "dev": true + }, + "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=", + "dev": true + }, + "is-decimal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", + "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", + "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-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true + }, + "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=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=" + }, + "is-hexadecimal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", + "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", + "dev": true + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, + "is-my-json-valid": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "dev": true + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=" + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true + }, + "is-path-inside": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "dev": true + }, + "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-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "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-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "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=", + "dev": true + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-root": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", + "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "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-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": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "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-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-whitespace-character": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", + "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs=", + "dev": true + }, + "is-word-character": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz", + "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs=", + "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": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=" + }, + "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-api": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.11.tgz", + "integrity": "sha1-/MC0YeKzvaceMFFVE4I4doJX2d4=", + "dev": true, + "dependencies": { + "istanbul-lib-instrument": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz", + "integrity": "sha1-6f2SDkdn89Ge3HZeLWs/XMvQ7qg=", + "dev": true + } + } + }, + "istanbul-lib-coverage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "integrity": "sha1-c7+5mIhSmUFck9OKPprfeEp3qdo=", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz", + "integrity": "sha512-3U2HB9y1ZV9UmFlE12Fx+nPtFqIymzrqCksrXujm3NVbAZIJg/RfYgO1XiIa0mbmxTjWpVEVlkIZJ25xVIAfkQ==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz", + "integrity": "sha1-6f2SDkdn89Ge3HZeLWs/XMvQ7qg=", + "dev": true + }, + "istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-tvF+YmCmH4thnez6JFX06ujIA19WPa9YUiwjc1uALF2cv5dmE3It8b5I8Ob7FHJ70H9Y5yF+TDkVa/mcADuw1Q==", + "dev": true + }, + "istanbul-lib-source-maps": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz", + "integrity": "sha512-mukVvSXCn9JQvdJl8wP/iPhqig0MRtuWuD4ZNKo6vB2Ik//AmhAKe3QnPN02dmkRe3lTudFk3rzoHhwU4hb94w==", + "dev": true + }, + "istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-P8G873A0kW24XRlxHVGhMJBhQ8gWAec+dae7ZxOBzxT4w+a9ATSPvRVK3LB1RAJ9S8bg2tOyWHAGW40Zd2dKfw==", + "dev": true + }, + "jest": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.4.tgz", + "integrity": "sha1-PdJgwpidba1nix6cxNkZRPbWAqw=", + "dev": true, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "jest-cli": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", + "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", + "dev": true + } + } + }, + "jest-changed-files": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz", + "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=", + "dev": true + }, + "jest-config": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.4.tgz", + "integrity": "sha1-43kwqyIXyRNgXv8T5712PsSPruo=", + "dev": true + }, + "jest-diff": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-20.0.3.tgz", + "integrity": "sha1-gfKI/Z5nXw+yPHXxwrGURf5YZhc=", + "dev": true + }, + "jest-docblock": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-20.0.3.tgz", + "integrity": "sha1-F76phDQswz2DxQ++FUXqDvqkRxI=", + "dev": true + }, + "jest-environment-jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz", + "integrity": "sha1-BIqKwS7iJfcZBBdxODS7mZeH3pk=", + "dev": true + }, + "jest-environment-node": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-20.0.3.tgz", + "integrity": "sha1-1Ii8RhKvLCRumG6K52caCZFj1AM=", + "dev": true + }, + "jest-haste-map": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-20.0.4.tgz", + "integrity": "sha1-ZT61XIic48Ah97lGk/IKQVm63wM=", + "dev": true + }, + "jest-jasmine2": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz", + "integrity": "sha1-/MWxQReA2RHQQpAu8YWehS5g1eE=", + "dev": true + }, + "jest-junit-reporter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-junit-reporter/-/jest-junit-reporter-1.1.0.tgz", + "integrity": "sha1-iNYAbsE/gt9AxHiCyGQJic3LFDQ=", + "dev": true + }, + "jest-matcher-utils": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz", + "integrity": "sha1-s6a443yld4A7CDKpixZPRLeBVhI=", + "dev": true + }, + "jest-matchers": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-matchers/-/jest-matchers-20.0.3.tgz", + "integrity": "sha1-ymnbHDLbWm9wf6XgQBq7VXAN/WA=", + "dev": true + }, + "jest-message-util": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-20.0.3.tgz", + "integrity": "sha1-auwoRDBvyw5udNV5bBAG2W/dgxw=", + "dev": true + }, + "jest-mock": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-20.0.3.tgz", + "integrity": "sha1-i8Bw6QQUqhVcEajWTIaaDVxx2lk=", + "dev": true + }, + "jest-regex-util": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-20.0.3.tgz", + "integrity": "sha1-hburXRM+RGJbGfr4xqpRItCF12I=", + "dev": true + }, + "jest-resolve": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-20.0.4.tgz", + "integrity": "sha1-lEiz6La6/BVHlETGSZBFt//ll6U=", + "dev": true + }, + "jest-resolve-dependencies": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz", + "integrity": "sha1-bhSntxevDyyzZnxUneQK8Bexcjo=", + "dev": true + }, + "jest-runtime": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-20.0.4.tgz", + "integrity": "sha1-osgCIZxCA/dU3xQE5JAYYWnRJNg=", + "dev": true, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "jest-snapshot": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-20.0.3.tgz", + "integrity": "sha1-W4R+GtsaTZCFKn+fElCG4YfHZWY=", + "dev": true + }, + "jest-util": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-20.0.3.tgz", + "integrity": "sha1-DAf32A2C9OWmfG+LnD/n9lz9Mq0=", + "dev": true + }, + "jest-validate": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-20.0.3.tgz", + "integrity": "sha1-0M/R3k9XnymEhJJcKA+PHZTsPKs=", + "dev": true + }, + "js-base64": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=", + "dev": true + }, + "js-file-download": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.1.tgz", + "integrity": "sha1-3g3S1mHVY19QanO5YqtY3bZQvts=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "js-yaml": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz", + "integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jschardet": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.1.tgz", + "integrity": "sha512-vE2hT1D0HLZCLLclfBSfkfTTedhVj0fubHpJBHKwwUWX0nSbhPAfk+SG9rTX95BYNmau8rGFfCeaT6T5OW1C2A==", + "dev": true + }, + "jsdom": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", + "integrity": "sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=", + "dev": true, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==" + }, + "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.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "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=" + }, + "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.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "jss": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-8.1.0.tgz", + "integrity": "sha512-NZ4CNAoPaPlM2rqHxPG5uGQbQEFZ9n1PITn0+wGIdAk2ZtA/F6el0SphLHf8So1Sx6N34hnVFFIuc32/hdsEzw==", + "dev": true + }, + "jss-camel-case": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/jss-camel-case/-/jss-camel-case-5.0.0.tgz", + "integrity": "sha512-vz11ip5EIlGuevtlUo9xIgiuD+it4Ebbb0+Y4o0A4oA8eOWY4aY7ihi/L7WvkQ54xnGOjUvLZ6nm2VYch2ufYg==", + "dev": true + }, + "jss-compose": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jss-compose/-/jss-compose-4.0.0.tgz", + "integrity": "sha512-VnsEziD2Lwrfwp10wx39FNybRLW5+RX/E2qQAXPAMbS+nHc0Jf2dC6ZiCfn5FaBGrpzLfIZ9MalTJDx4CQoMAQ==", + "dev": true + }, + "jss-default-unit": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jss-default-unit/-/jss-default-unit-7.0.0.tgz", + "integrity": "sha512-U1Oi1h45vFRuISr+g1DQ3Oua7CkNKNs47fTdiT/lHkuBMc6BBDUbPv9IbPPhk9gsEaX45Iy9TX8CAuaHLPCfEA==", + "dev": true + }, + "jss-global": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jss-global/-/jss-global-2.0.0.tgz", + "integrity": "sha512-/FSOMp4lF/vg47T/w8kKvL9tu7ka9am8N4izS63W81Qlay9hAq6xe9RxrPxygLpnn4KEb8LNbkKRoUv4SJfQsQ==", + "dev": true + }, + "jss-isolate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jss-isolate/-/jss-isolate-4.0.0.tgz", + "integrity": "sha512-bVvWcQj+Jeso8yGcYqnw/h5klf8+ur3Ytr7GG4JQj9TDhueJSvxctoATdfbcZiX72897xvdsDE0OjKUYuilNkQ==", + "dev": true + }, + "jss-nested": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/jss-nested/-/jss-nested-5.0.0.tgz", + "integrity": "sha512-9Molau+XVpSc6QEco3EC5yXmzeGMc5ZVII8+qy6jD6bvu6Y9mpfGoJ00LalR/n7xr/LC7Cxgs44UQQlLzumMBg==", + "dev": true + }, + "jstransform": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", + "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", + "dependencies": { + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", + "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" + }, + "source-map": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", + "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=" + } + } + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", + "dev": true + }, + "keycode": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz", + "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo=" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true + }, + "latest-version": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", + "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lazy-req": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", + "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=" + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true + }, + "lie": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.0.2.tgz", + "integrity": "sha1-/9oh17uibzd8rYZdNkmy/Izjn+o=" + }, + "listify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/listify/-/listify-1.0.0.tgz", + "integrity": "sha1-A8p7otFQ1CZ3c/dOV1WNEFPSvuM=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=" + }, + "loader-fs-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", + "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", + "dev": true + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=" + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + }, + "localforage": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.5.0.tgz", + "integrity": "sha1-a5lOGbVmEfqF3zmS3zl6xKtm6BU=" + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "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 + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash-es": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz", + "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc=" + }, + "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.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "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.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "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.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "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.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.0", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz", + "integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU=" + }, + "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.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "dev": true + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "dev": true + }, + "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 + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "longest-streak": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.1.tgz", + "integrity": "sha1-QtKRtUEeQDZcAOYxk0l+IkcxbjU=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true + }, + "macaddress": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", + "dev": true + }, + "magic-string": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.14.0.tgz", + "integrity": "sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI=", + "dev": true + }, + "make-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", + "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "markdown-escapes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", + "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=", + "dev": true + }, + "markdown-table": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.1.tgz", + "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", + "dev": true + }, + "markdown-to-jsx": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-5.4.1.tgz", + "integrity": "sha512-C1bOuwvZ36MKE+c0m0SH98Dv4xCvmXxL+TvOyXL5q6NklnlAMJ9Mn94kAA6OpFnUCKiZf/vhPF+GOupkKZjKVg==", + "dev": true + }, + "material-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.19.0.tgz", + "integrity": "sha1-l4HdWtNAr6BSAwke4fb7jTKlq0E=" + }, + "material-ui-superselectfield": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/material-ui-superselectfield/-/material-ui-superselectfield-1.5.6.tgz", + "integrity": "sha512-45i+o+Tq+PAUv1CaodR+u/unGpUJroIQDuHQT7NuODLrOcscWj1JDH2o2rkEkH9qSczZ9f5Plq8v4Lj++wqALw==" + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", + "dev": true + }, + "mdast-util-compact": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", + "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", + "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 + }, + "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=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "merge": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", + "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", + "dev": true + }, + "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 + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=" + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=" + }, + "mime": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=", + "dev": true + }, + "mime-db": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", + "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", + "dev": true + }, + "mime-types": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", + "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", + "dev": true + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=" + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + }, + "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=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "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 + }, + "ncname": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", + "dev": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "no-case": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=", + "dev": true + }, + "node-dir": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", + "integrity": "sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=", + "dev": true + }, + "node-fetch": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz", + "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==" + }, + "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.0.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "node-notifier": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.1.2.tgz", + "integrity": "sha1-L6nhJgX6EACdRFSdb82KY93g5P8=", + "dev": true + }, + "node-status-codes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==" + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=" + }, + "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 + }, + "nth-check": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "dev": true + }, + "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=" + }, + "nwmatcher": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.1.tgz", + "integrity": "sha1-eumwew6oBNt+JfBctf5Al9TklJ8=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "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-hash": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", + "integrity": "sha1-KKZZz5h9lqTavnhgKJ87UybEoDw=", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object.assign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", + "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", + "dev": true + }, + "object.entries": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", + "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=" + }, + "object.values": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", + "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", + "dev": true + }, + "obuf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "integrity": "sha1-EEEktsYCxnlogaBCVB0220OlJk4=", + "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 + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "open": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=", + "dev": true + }, + "openseadragon": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/openseadragon/-/openseadragon-2.3.1.tgz", + "integrity": "sha1-79z+4Z2LPEbQDX992nh8cFf2g2Q=" + }, + "opn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", + "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", + "dev": true + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "original": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "dev": true, + "dependencies": { + "url-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", + "dev": true + } + } + }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=" + }, + "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 + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true + }, + "p-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", + "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", + "dev": true + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true + }, + "p-map": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.1.1.tgz", + "integrity": "sha1-BfXkrpegaDcbwqXMhr+9vBnErno=", + "dev": true + }, + "package-json": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", + "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", + "dev": true + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "paper": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/paper/-/paper-0.11.4.tgz", + "integrity": "sha512-oSkKKeL1+wCEccBMxHmjxwIb5pjkxZ7Q+LiLgTEl6fxUDjEUHO9Dj/XyQmpZvbwUUAW7bv7dLhXB9w1NVn60PQ==" + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=" + }, + "parse-entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", + "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=" + }, + "parse5": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", + "dev": true + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=" + }, + "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=" + }, + "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-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "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=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=" + }, + "pbkdf2": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.13.tgz", + "integrity": "sha512-+dCHxDH+djNtjgWmvVC/my3SYBAKpKNqKSjLkp+GtWWYe4XPE+e/PSD2aCanlEZZnqPk2uekTKNC/ccbwd2X2Q==" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" + }, + "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 + }, + "pkg-up": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", + "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", + "dev": true + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "postcss": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.8.tgz", + "integrity": "sha512-G6WnRmdTt2jvJvY+aY+M0AO4YlbxE+slKPZb+jG2P2U9Tyxi3h1fYZ/DgiFU6DC6bv3XIEJoZt+f/kNh8BrWFw==", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "supports-color": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz", + "integrity": "sha512-qxzYsob3yv6U+xMzPrv170y8AwGP7i74g+pbixCfD6rgso8BscLT2qXIuz6TpOaiJZ3mFgT5O9lyT9nMU4LfaA==", + "dev": true + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-flexbugs-fixes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.0.0.tgz", + "integrity": "sha1-ezHLbCfQQXo1pnkUwpX4PEA8ftQ=", + "dev": true + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "dev": true + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "dev": true + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "dev": true + }, + "postcss-loader": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.5.tgz", + "integrity": "sha1-wZ0+i4PrGsMW9WIe9MDvWz0bizo=", + "dev": true + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true + }, + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "dependencies": { + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + } + } + }, + "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 + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", + "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 + }, + "pretty-format": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz", + "integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true + } + } + }, + "private": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" + }, + "prop-types": { + "version": "15.5.10", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=" + }, + "proxy-addr": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", + "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", + "dev": true + }, + "prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "q": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", + "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "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 + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=" + } + } + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "react": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", + "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=" + }, + "react-addons-test-utils": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/react-addons-test-utils/-/react-addons-test-utils-15.6.0.tgz", + "integrity": "sha1-Bi02EX/o0Y87peBuszODsLhepbk=", + "dev": true + }, + "react-codemirror": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-codemirror/-/react-codemirror-1.0.0.tgz", + "integrity": "sha1-kUZ7U7H12A2Rai/QtMetuFqQAbo=", + "dev": true + }, + "react-dev-utils": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-3.0.2.tgz", + "integrity": "sha1-GkImPptqoR3LRdad/l6xs1S9VTE=", + "dev": true, + "dependencies": { + "ansi-escapes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", + "integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true + }, + "inquirer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz", + "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==", + "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 + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "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, + "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 + } + } + } + } + }, + "react-dnd": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.5.4.tgz", + "integrity": "sha512-y9YmnusURc+3KPgvhYKvZ9oCucj51MSZWODyaeV0KFU0cquzA7dCD1g/OIYUKtNoZ+MXtacDngkdud2TklMSjw==", + "dependencies": { + "hoist-non-react-statics": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", + "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=" + } + } + }, + "react-dnd-html5-backend": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.5.4.tgz", + "integrity": "sha512-jDqAkm/hI8Tl4HcsbhkBgB6HgpJR1e+ML1SbfxaegXYiuMxEVQm0FOwEH5WxUoo6fmIG4N+H0rSm59POuZOCaA==" + }, + "react-docgen": { + "version": "3.0.0-beta6", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-beta6.tgz", + "integrity": "sha1-KCe1LFK4ZEci4OIVxb98FcBFjDQ=", + "dev": true, + "dependencies": { + "ast-types": { + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.11.tgz", + "integrity": "sha1-NxF3u1kjL/XOqh0J7lytcFsaWqk=", + "dev": true + }, + "babylon": { + "version": "7.0.0-beta.17", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.17.tgz", + "integrity": "sha1-Kq1NZ2T0Cd+zrCFthV3JPXDTeRE=", + "dev": true + }, + "core-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", + "dev": true + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "recast": { + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.6.tgz", + "integrity": "sha1-Sw+4L+sdELO9YtNJQ0Jtmz7TDUw=", + "dev": true + } + } + }, + "react-docgen-displayname-handler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-docgen-displayname-handler/-/react-docgen-displayname-handler-1.0.0.tgz", + "integrity": "sha1-n8Gwcc+q0B4lRrJYOC3sM8WeEHs=", + "dev": true, + "dependencies": { + "ast-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.0.tgz", + "integrity": "sha1-yHIch0euTVspuSnpnFMXtOh0ViM=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "recast": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.12.tgz", + "integrity": "sha1-p55NP4LV1yqC7hd66qeR55O75dY=", + "dev": true + } + } + }, + "react-dom": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", + "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=" + }, + "react-dropzone": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-4.1.3.tgz", + "integrity": "sha512-AmVy1hSZ/BELkr1Pta8n+zFBfR7nZugU/hlyjauooozcBdL+d6xUMV/5xSQE/qxkKjPC7wtE/XPJUv54gLsezA==" + }, + "react-error-overlay": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-1.0.9.tgz", + "integrity": "sha1-mI5I9vNDr6l6cZxN2uUbj+jM/ug=", + "dev": true, + "dependencies": { + "anser": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.2.5.tgz", + "integrity": "sha1-Xc/JVuqjc7nCMBDdINq+ws4ZR1s=", + "dev": true + }, + "babel-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", + "dev": true, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + } + } + }, + "react-event-listener": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.4.5.tgz", + "integrity": "sha1-4+iVoJcM8U7o+JAROvaBl6vz0LE=" + }, + "react-group": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-group/-/react-group-1.0.5.tgz", + "integrity": "sha1-ygfWjLuubZklCCnhwy94JYdpPAc=", + "dev": true + }, + "react-icon-base": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.0.7.tgz", + "integrity": "sha1-C9GHNr1s55ym1pzoOHoH+41M7/4=", + "dev": true, + "dependencies": { + "prop-types": { + "version": "15.5.8", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.8.tgz", + "integrity": "sha1-a3suFBCDvjjIWVqlH8VXdccZk5Q=", + "dev": true + } + } + }, + "react-icons": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.5.tgz", + "integrity": "sha1-+UJQHCGkzARWziu+5QMsk/YFHc8=", + "dev": true + }, + "react-infinite": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/react-infinite/-/react-infinite-0.12.1.tgz", + "integrity": "sha512-sOXsm0OsszFQQ+4Vtqt1UUqLETGOCS0keAdEQuNMmeoIHHz2iIW44cHhPLxyeAsdfJQOYanmBZjhpZFQw7bhKw==", + "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-redux": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.5.tgz", + "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=" + }, + "react-router": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.1.1.tgz", + "integrity": "sha1-1Ejzt8G0Kab7sDOVCZlJxgax/pU=" + }, + "react-router-dom": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.1.1.tgz", + "integrity": "sha1-MCGt4fLBYK+Xz5TiVZTF8pRYMCU=" + }, + "react-scripts": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.7.tgz", + "integrity": "sha1-/hQ23aA7tFRlx20JfP6k8y63y7s=", + "dev": true, + "dependencies": { + "babel-core": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", + "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=", + "dev": true + }, + "babel-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.0.0.tgz", + "integrity": "sha1-LkOma+4f/0RwUz0EAsikUy+vuvc=", + "dev": true + }, + "babel-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", + "dev": true, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "jest": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.3.tgz", + "integrity": "sha1-5P0FTE8RcKEWoAdh2kz9tz8c3DM=", + "dev": true, + "dependencies": { + "jest-cli": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", + "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", + "dev": true + } + } + }, + "promise": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", + "dev": true + }, + "source-list-map": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true + } + } + }, + "webpack": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", + "integrity": "sha1-LgRX8KuxrF3zqxBsacZy8jZ4Xwc=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true + } + } + }, + "webpack-sources": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", + "dev": true + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + } + } + }, + "react-styleguidist": { + "version": "5.5.9", + "resolved": "https://registry.npmjs.org/react-styleguidist/-/react-styleguidist-5.5.9.tgz", + "integrity": "sha512-UO2V+OWBzLzvJB0uC+kjZENbGeOvdtTS2nr4HwjzoXcurHyFcMxyI2ACWqhYnLeJqsiqayN4dFXObsp17eMzIQ==", + "dev": true, + "dependencies": { + "ansi-html": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.5.tgz", + "integrity": "sha1-DcqloIEgaGa8JAo7dzoYTqO4i2Q=", + "dev": true + }, + "ast-types": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.12.tgz", + "integrity": "sha1-sTYwDWcCZiWuFTJpgsqZGOXbc8k=", + "dev": true + }, + "css-loader": { + "version": "0.28.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.4.tgz", + "integrity": "sha1-bPNXkZLONV6LONX0Ldeh8uyJjQ8=", + "dev": true + }, + "faye-websocket": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.7.3.tgz", + "integrity": "sha1-zEB0x/Sk39A69U3WXDVLE1EyzhE=", + "dev": true + }, + "html-entities": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.0.tgz", + "integrity": "sha1-QZSMr4XOgv7Tbk5qDtNxpmZDeeI=", + "dev": true + }, + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "dev": true + }, + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dev": true + }, + "react-dev-utils": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-0.5.2.tgz", + "integrity": "sha1-UNC5YtOpS2wujyAR7WRo5BJLxBA=", + "dev": true + }, + "recursive-readdir": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.1.1.tgz", + "integrity": "sha1-oBz8f384pT7AlqCW9jpQSJw+KXw=", + "dev": true + }, + "sockjs-client": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.0.1.tgz", + "integrity": "sha1-iUOuBbRlR7wgVIFsQJACz14v4CY=", + "dev": true + }, + "webpack-dev-server": { + "version": "1.16.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-1.16.5.tgz", + "integrity": "sha1-DL1fLSrI1OWTqs1clwLnu9XlmJI=", + "dev": true, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "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=" + }, + "react-test-renderer": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-15.6.1.tgz", + "integrity": "sha1-Am9KW7VVJmH9LMS7zQ1LyKNev34=", + "dev": true + }, + "react-transition-group": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.0.tgz", + "integrity": "sha1-tR/JIbDDg1p+98Vxx5/ILHPpIE8=" + }, + "read-all-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=" + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" + }, + "readable-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=" + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true + }, + "recast": { + "version": "0.10.43", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.43.tgz", + "integrity": "sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8=", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", + "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true + }, + "recompose": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.24.0.tgz", + "integrity": "sha512-7+UVym5Mfks/ukIDfcAiasrY61YGki8uIs4CmLTGU7UV2lm2ObbhOl913WrlsZKu8x8uA/sLJUOI5hxVga0dIA==" + }, + "recursive-readdir": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz", + "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=", + "dev": true, + "dependencies": { + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "dev": true, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "redux": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.1.tgz", + "integrity": "sha512-iEVTlORM5mv6xb3ZAOyrVehVUD+W87jdFAX6SYVgZh3/SQAWFSxTRJOqPWQdvo4VN4lJkNDvqKlBXBabsJTSkA==" + }, + "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.2", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz", + "integrity": "sha1-4Pmo6N/KfBe+kscSSVijuU6ykR0=" + }, + "redux-mock-store": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.2.3.tgz", + "integrity": "sha1-GzrSmdqRy0G6MNaOO28CRHX7nhs=", + "dev": true + }, + "redux-persist": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.8.2.tgz", + "integrity": "sha1-eUEgLgzgqfzAJj1mll9SY9NPQ7k=" + }, + "regenerate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, + "regenerator-transform": { + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz", + "integrity": "sha1-On0GdSDLe3F2dp61/4aGkb7+EoM=", + "dev": true + }, + "regex-cache": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=" + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true + }, + "registry-auth-token": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", + "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", + "dev": true + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true + }, + "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, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remark": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-7.0.1.tgz", + "integrity": "sha1-pd5NrPq/D2CkmCbvJMR5gH+QS/s=", + "dev": true + }, + "remark-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-3.0.1.tgz", + "integrity": "sha1-G5+EGkTY9PvyJGhQJlRZpOs1TIA=", + "dev": true + }, + "remark-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-3.0.1.tgz", + "integrity": "sha1-eSQr6+CnUggbWAlRb6DAbt7Aac8=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", + "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=" + }, + "renderkid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", + "dev": true, + "dependencies": { + "utila": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", + "dev": true + } + } + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, + "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=" + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "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.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "dev": true + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "resolve-pathname": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.1.0.tgz", + "integrity": "sha1-6DWIAbhrg7F1YNTjw4LXrvIQCUQ=" + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=" + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=" + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sane": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-1.6.0.tgz", + "integrity": "sha1-lhDEUjB6E10pwf3+JUcDQYDEZ3U=", + "dev": true, + "dependencies": { + "bser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", + "integrity": "sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=", + "dev": true + }, + "fb-watchman": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.2.tgz", + "integrity": "sha1-okz0eCf4LTj7Waaa1wt247auc4M=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "dependencies": { + "ajv": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true + }, + "semver-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.1.tgz", + "integrity": "sha1-J9kv7DTSfPpCcH07QNAlrphV8t8=", + "dev": true + }, + "send": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", + "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", + "dev": true, + "dependencies": { + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true + } + } + }, + "serve-index": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", + "integrity": "sha1-0rKA/FYNYW7oG0i/D6gqvtJIXOc=", + "dev": true + }, + "serve-static": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", + "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", + "dev": true + }, + "serviceworker-cache-polyfill": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", + "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "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=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, + "settle-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/settle-promise/-/settle-promise-1.0.0.tgz", + "integrity": "sha1-aXrbWLgh84fOJ1fAbvyd5fDuM9g=", + "dev": true + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=" + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true + }, + "shellwords": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.0.tgz", + "integrity": "sha1-Zq/Ue2oSky2Qccv9mKUueFzQuhQ=", + "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=" + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true + }, + "sockjs": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", + "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", + "dev": true, + "dependencies": { + "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 + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "dev": true + }, + "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 + }, + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + }, + "source-map-support": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", + "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", + "dev": true + }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=" + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "spdy": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "dev": true + }, + "spdy-transport": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", + "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", + "dev": true + }, + "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.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "state-toggle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", + "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", + "dev": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=" + }, + "stream-cache": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream-cache/-/stream-cache-0.0.2.tgz", + "integrity": "sha1-GsWtaDJCjKVWZ9ve45Xa1ObbEY8=", + "dev": true + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=" + }, + "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_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==" + }, + "string-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", + "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" + }, + "stringify-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", + "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "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=" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "style-loader": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", + "integrity": "sha1-6CVLzNt690vVgnTjYQe01atN8xA=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true + } + } + }, + "sw-precache": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.0.tgz", + "integrity": "sha1-62IlzlgM6q4UgZRXigrQGrfqGZw=", + "dev": true + }, + "sw-precache-webpack-plugin": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.9.1.tgz", + "integrity": "sha1-I4H/cG+7bKvbIKIDN96OWPtJoqc=", + "dev": true, + "dependencies": { + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true + } + } + }, + "sw-toolbox": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", + "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=", + "dev": true + }, + "symbol-observable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", + "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "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 + }, + "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": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "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 + } + } + }, + "tapable": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", + "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" + }, + "test-exclude": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", + "integrity": "sha1-TYSWSwlmsAh+zDNKLOAC09k0HiY=", + "dev": true + }, + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", + "integrity": "sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "timed-out": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.3.tgz", + "integrity": "sha512-+JAqyNgg+M8+gXIrq2EeUr4kZqRz47Ysco7X5QKRGScRE9HIHckyHD1asozSFGeqx2nmPCgA8T5tIGVO0ML7/w==" + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true + }, + "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=" + }, + "to-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-ast/-/to-ast-1.0.0.tgz", + "integrity": "sha1-DEoxyMmO396arwGSx5S0yLEe4oc=", + "dev": true, + "dependencies": { + "ast-types": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", + "integrity": "sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "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 + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "toposort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", + "integrity": "sha1-8CzYp0vYvi/A6YYRw7rLlaFxhpw=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "trim-trailing-lines": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", + "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=", + "dev": true + }, + "trough": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.1.tgz", + "integrity": "sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=", + "dev": true + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": 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 + }, + "type-detect": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", + "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", + "dev": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true + }, + "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.14", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", + "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=" + }, + "uglify-js": { + "version": "3.0.27", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.27.tgz", + "integrity": "sha512-HD8CmxPXUI62v5tweiulMcP/apAtx1DXGcNZkhKQZyC+MTrTsoCBb8yPAwVrbvpgw3EpRU76bRe6axjIiCYcQg==", + "dev": true + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dependencies": { + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=" + }, + "webpack-sources": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", + "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=" + } + } + }, + "unherit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", + "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=", + "dev": true + }, + "unified": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.5.tgz", + "integrity": "sha1-cWk3hyYhpjE15iztLzrGoGPG+4c=", + "dev": true + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqid": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unist-util-modify-children": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz", + "integrity": "sha1-ZtfmpEnm9nIguXarPLi166w55R0=", + "dev": true + }, + "unist-util-remove-position": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", + "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=", + "dev": true + }, + "unist-util-stringify-position": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz", + "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw=", + "dev": true + }, + "unist-util-visit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.1.3.tgz", + "integrity": "sha1-7CaOcxudJ3p5pbWqBkOZDkBdYAs=", + "dev": true + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unreachable-branch-transform": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz", + "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=" + }, + "unzip-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", + "dev": true + }, + "update-notifier": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", + "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "urijs": { + "version": "1.18.10", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.18.10.tgz", + "integrity": "sha1-uURj6rpZoaeWA2pGe7YzxmfyIas=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", + "integrity": "sha1-uRg7GAHg+EdxhnNnMEC8ncHHFcU=", + "dev": true + }, + "url-parse": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", + "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", + "dev": true, + "dependencies": { + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", + "dev": true + } + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "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.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "dev": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=" + }, + "value-equal": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.2.1.tgz", + "integrity": "sha1-wiCjBDYfzmmU277ao8fhobiVhx0=" + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", + "dev": true + }, + "vendors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", + "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", + "dev": true + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true + }, + "vfile": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.2.0.tgz", + "integrity": "sha1-zkek+zNZIrIz5TXbD32BIdj87U4=", + "dev": true + }, + "vfile-location": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz", + "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU=", + "dev": true + }, + "vlq": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", + "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=" + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=" + }, + "watch": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", + "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=", + "dev": true + }, + "watchpack": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", + "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=" + }, + "wbuf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", + "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", + "dev": true + }, + "webidl-conversions": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.1.tgz", + "integrity": "sha1-gBWherg+fhsxFjhIas6B2mziBqA=", + "dev": true + }, + "webpack": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.0.0.tgz", + "integrity": "sha1-7pvOvyEkf3FTy0EBaMq0XjpZ1Nc=", + "dependencies": { + "ajv": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.0.tgz", + "integrity": "sha1-wXNQJMXaLvdcwZBxMHPUTwmL9IY=" + }, + "ajv-keywords": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.0.tgz", + "integrity": "sha1-opbhf3v658HOT34N5T0pyzIWLfA=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=" + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" + }, + "webpack-sources": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", + "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==" + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=" + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=" + } + } + }, + "webpack-dev-middleware": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz", + "integrity": "sha1-CWkdCXOjCtH4Ksc6EuIIfwpHVPk=", + "dev": true + }, + "webpack-dev-server": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", + "integrity": "sha1-MThM6BE2vhCAtLTN4OubkOVO5s8=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "dev": true + }, + "sockjs-client": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", + "integrity": "sha1-8CEqhVDkyUaMjM6u79LjSTwDOtU=", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true + } + } + }, + "webpack-manifest-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz", + "integrity": "sha1-a2xxiq3oolN5lXhLRr0umDYFfKo=", + "dev": true, + "dependencies": { + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true + } + } + }, + "webpack-merge": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.0.tgz", + "integrity": "sha1-atciI7PguDflMeRZfBmfkJNhUR4=", + "dev": true + }, + "webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", + "dev": true + }, + "webpage": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/webpage/-/webpage-0.3.0.tgz", + "integrity": "sha1-FcjJnoIrSZ6Zga5odlObQjRIuOc=", + "dev": true + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true + }, + "websocket-extensions": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz", + "integrity": "sha1-PGxFGhmO567FWx7GHQkgxngBpfQ=", + "dev": true, + "dependencies": { + "iconv-lite": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", + "dev": true + } + } + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "whatwg-url": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", + "integrity": "sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=", + "dev": true, + "dependencies": { + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + } + } + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "widest-line": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", + "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "worker-farm": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.4.1.tgz", + "integrity": "sha512-tgFAtgOYLPutkAyzgpS6VJFL5HY+0ui1Tvua+fITgz8ByaJTMFGtazR6xxQfwfiAcbwE+2fLG/K49wc2TfwCNw==", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true + }, + "x-is-function": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", + "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=", + "dev": true + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "dev": true + }, + "xdg-basedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", + "dev": true + }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "dev": true + }, + "xml-char-classes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=", + "dev": true + }, + "xml-name-validator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", + "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + } + } +} diff --git a/viscoll-app/package.json b/viscoll-app/package.json new file mode 100644 index 00000000..5020f0f1 --- /dev/null +++ b/viscoll-app/package.json @@ -0,0 +1,71 @@ +{ + "name": "viscoll-app", + "version": "0.1.0", + "private": true, + "dependencies": { + "axios": "^0.16.2", + "babel-polyfill": "^6.26.0", + "clientjs": "^0.1.11", + "copy-to-clipboard": "^3.0.8", + "es6-promise": "^4.1.0", + "fuse.js": "^3.0.5", + "immutability-helper": "^2.4.0", + "js-file-download": "^0.4.1", + "localforage": "^1.5.0", + "material-ui": "^0.19.0", + "material-ui-superselectfield": "^1.5.6", + "openseadragon": "^2.3.1", + "paper": "^0.11.4", + "react": "^15.6.1", + "react-dnd": "^2.5.4", + "react-dnd-html5-backend": "^2.5.4", + "react-dom": "^15.6.1", + "react-dropzone": "^4.1.3", + "react-redux": "^5.0.5", + "react-router-dom": "^4.1.1", + "react-tap-event-plugin": "^2.0.1", + "redux": "^3.7.1", + "redux-axios-middleware": "^4.0.0", + "redux-devtools-extension": "^2.13.2", + "redux-persist": "^4.8.2", + "webpack": "^3.0.0" + }, + "devDependencies": { + "babel-jest": "^20.0.3", + "babel-loader": "^7.1.1", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", + "enzyme": "^2.9.1", + "jest": "^20.0.4", + "jest-junit-reporter": "^1.1.0", + "react-addons-test-utils": "^15.6.0", + "react-scripts": "1.0.7", + "react-styleguidist": "^5.5.9", + "react-test-renderer": "^15.6.1", + "redux-mock-store": "^1.2.3", + "regenerator-runtime": "^0.11.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "jest", + "eject": "react-scripts eject", + "styleguide": "styleguidist server", + "styleguide:build": "styleguidist build" + }, + "babel": { + "presets": [ + "react", + "es2015", + "stage-2" + ] + }, + "jest": { + "testResultsProcessor": "./node_modules/jest-junit-reporter", + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/assetsTransformer.js", + "\\.(css|less)$": "/assetsTransformer.js" + } + } +} diff --git a/viscoll-app/public/favicon.ico b/viscoll-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..58182242e3888c9824ef6edb2f4947ad0d83ac3a GIT binary patch literal 1150 zcmcIk&1(};5PyOKDOKJzTLN*pEGYaWdpSb!3pzT-ei6=zxg0S9HCENpP=)kxO!BG z6GDhnq+}vXV5dt6`tA6!K=qpr{3q#jI_UU5e9wbY3d(iCw!_3He@9BVF{EMxD9mIat+p;h5|-;*Tbf7m=6RjBkez~5#8Xjh*MlWp zt+u03Eq_I=v4xhTdbweafbh8;_y7vOr~U x*AJ53{_J~%b=NCvs4nLsJoY_$zHoe + + + + + + + + + + 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..dec30f2d --- /dev/null +++ b/viscoll-app/sass/components/_dialog.scss @@ -0,0 +1,75 @@ +.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 { + .label { + width: 100px; + display:inline-block; + vertical-align: top; + } + .input { + width: 200px; + display:inline-block; + text-align: right; + } +} +.newProjectDialog { + h1 { + font-weight: normal; + text-transform: inherit; + } + .newProjectSelection { + .selectItem { + display: flex; + padding: 1.9em 0em; + @include transition(all, 200ms, ease-in-out); + border: 2px solid $white; + + &:hover { + border: 2px solid $teal; + cursor: pointer; + } + + .icon { + width: 50px; + padding:0px 15px; + } + .text { + h1 { + font-size: 2em; + margin:0; + } + h2 { + font-size: 1em; + color: lighten($black, 25); + padding-top:5px; + margin:0; + } + } + } + } +} \ 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..c0127dd6 --- /dev/null +++ b/viscoll-app/sass/index.scss @@ -0,0 +1,21 @@ +@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/imageManager'; +@import 'components/dialog'; +@import 'components/tooltip'; +@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/_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/_imageManager.scss b/viscoll-app/sass/layout/_imageManager.scss new file mode 100644 index 00000000..288c2856 --- /dev/null +++ b/viscoll-app/sass/layout/_imageManager.scss @@ -0,0 +1,199 @@ +.imageManager { + .form { + .row { + display: flex; + .label { + padding-top:1em; + min-width: 120px; + } + .input { + flex-grow: 1; + } + } + } + .manageManifests { + padding: 0em 2em; + .manifestCard { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + padding: 1.5em 1.5em 1em 1.5em; + font-size: 1.1em; + font-weight: 500; + span { + font-size: 0.8em; + font-weight: normal; + color: transparentize($black, 0.4); + } + &>div { + flex-grow: 1; + } + .thumbnails { + text-align: right; + } + } + } + .imageMapper { + .draggableItem { + height: 50px; + background: $white; + border-width: 0px 1px 0px 1px; + border-style: solid; + border-color: $gray; + cursor: move; + @include border-radius(3px); + padding-left: 0.5em; + + + .text { + display: inline-block; + color: $black; + padding-left: 0.5em; + @include centerize(0%, 50%); + &>span { + font-size: 0.8em; + color: transparentize($black, 0.3); + } + } + .thumbnail { + opacity: 0.5; + display: inline-block; + width: 1.5em; + @include transition(all, 200ms, ease-in-out); + + &:hover { + opacity: 1; + cursor: pointer; + } + } + } + + .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%; + height: 40vh; + margin-top: 1em; + margin-left: 1em; + background: darken($gray, 3); + color: $black; + &>div { + // display: block; + width: 100%; + height: 100%; + margin: 0em; + // padding: 0.5em; + } + .panelBarGroup { + height: 50px; + display: flex; + } + .boards { + display: flex; + width: 100%; + overflow-y: auto; + &>div { + width: 50%; + } + } + .binText { + @include centerize(50%,50%); + text-transform: uppercase; + text-align: center; + } + } + .bottomPanel { + flex-grow: 2; + display: flex; + justify-content: space-between; + width: 97%; + // height: 42vh; + margin-top: 1em; + margin-left: 1em; + // background: red; + .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; + + .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; + width: 100%; + height: 50px; + display: flex; + 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 { + text-align: right; + } + &>div { + width: 40%; + } + } + } +} \ 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..dae95bd1 --- /dev/null +++ b/viscoll-app/sass/layout/_infobox.scss @@ -0,0 +1,30 @@ +.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; + + .row { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + } + .label { + width: 35%; + } + .input { + width: 55%; + } + } +} diff --git a/viscoll-app/sass/layout/_landing.scss b/viscoll-app/sass/layout/_landing.scss new file mode 100644 index 00000000..f5c818c7 --- /dev/null +++ b/viscoll-app/sass/layout/_landing.scss @@ -0,0 +1,60 @@ +.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 { + width: 100%; + height:10vh; + background: $teal; + } + + 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..2d27efc1 --- /dev/null +++ b/viscoll-app/sass/layout/_notes.scss @@ -0,0 +1,112 @@ +.notesManager { + height: 100%; + .container { + padding: 1em 2em 0em 2em; + } + + + .browse { + height: 100%; + .notesList { + .item { + margin: 1em; + padding: 1em; + display: block; + background: $gray; + cursor: pointer; + @include transition(background, 200ms, ease-in-out); + + &:hover { + background: lighten($gray, 4); + } + &.active { + font-weight: 600; + background: $teal; + } + &.add { + border: 1px solid lighten($success, 30); + background: lighten($gray, 10); + + &.active { + background: $success; + color: $white; + } + } + } + } + .details { + position: relative; + left: 256px; + width: 65%; + } + } + .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; + + .searchTextbox { + padding-top: 5px; + } +} +.searchOptions { + visibility: hidden; + opacity: 0; + background: $white; + @include border-radius(0px 0px 6px 6px); + @include transition(all, 200ms, ease-in-out); + + 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%; + } + .input { + width: 80%; + .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..73232cb1 --- /dev/null +++ b/viscoll-app/sass/layout/_sidebar.scss @@ -0,0 +1,90 @@ +.sidebar { + position: fixed; + display: block; + top: 55px; + width: 18%; + height: 100%; + background: $bg_blue; + opacity: 1; + @include transition(all, 200ms, ease-in-out); + + &.hidden { + opacity: 0; + } + hr { + border: 1px solid $teal; + margin: 0; + } + h1 { + color: $white; + font-size: 1em; + font-weight: lighter; + } + .selectMode { + padding: 1em 1em 2em 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%); + } + .manager { + text-align: center; + padding: 0.5em 0em; + cursor: pointer; + margin: 0px -16px; + text-transform: uppercase; + @include transition(all, 100ms, ease-in-out); + + &:hover { + background: transparentize($white,0.98); + font-weight: bold; + } + + &.active { + background: transparentize($white,0.95); + font-weight: bold; + border-left: 3px solid $teal; + } + } + .export { + div + div { + margin-top: 10px; + } + } + +} + + + +.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..f445467c --- /dev/null +++ b/viscoll-app/sass/layout/_tabular.scss @@ -0,0 +1,163 @@ +.itemContainer { + display: flex; + align-items: stretch; + &.group { + margin-top: -21px; + @include transition(background, 100ms, ease-in-out); + &:hover { + background: lighten($teal, 30) !important; + cursor: pointer; + } + &.active:hover { + background: $teal !important; + } + } +} +.leafSection { + display: flex; + flex-grow: 1; + border: 1px solid $white; + @include transition(background, 100ms, ease-in-out); + &:hover { + background: lighten($teal, 30); + cursor: pointer; + } + &.active:hover { + background: $teal !important; + } +} +.itemName { + width: 70px; + display: flex; + align-items: center; + font-weight: 500; + padding-left: 20px; + min-height: 45px; +} +.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; + + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11px; + } + + 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); + + .side { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + border: 1px solid $white; + + &:hover { + background: lighten($teal, 30); + cursor: pointer; + } + .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: 1px solid transparentize($black, 0.85); + } + } +} + +.sideToggle { + width: 20px; + height: 44px; + border-left: 1px solid transparentize($black, 0.85); + .side { + display: block; + width: 20px; + height: 20px; + padding-top: 2px; + padding-left: 5px; + color: transparentize($black, 0.4); + &:first-child { + border-bottom: 1px solid transparentize($black, 0.85); + } + &:hover { + background: lighten($teal, 20); + cursor: pointer; + 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..deb9b2b9 --- /dev/null +++ b/viscoll-app/sass/layout/_topbar.scss @@ -0,0 +1,25 @@ +.topbar { + position: fixed; + left:0px; + width: 100%; + z-index: 100; + @include box-shadow(0px 1px 2px 1px rgba(0,0,0,0.05)); + + .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%); + } + } +} \ 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..bd86834e --- /dev/null +++ b/viscoll-app/sass/layout/_workspace.scss @@ -0,0 +1,34 @@ +.workspace { + position: absolute; + left: 18%; + top: 56px; +} +.projectWorkspace { + @extend .workspace; + width: 54%; + margin: 2%; + .viewingMode { + display: flex; + &>div:first-child { + margin-top: 4px; + margin-left: 15px; + } + &>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..d7684a94 --- /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: #D87979; +$success: #34A251; +$warning: #E37A05; + +// Breakpoints +$medium: max-width 1200px; +$small: max-width 992px; +$xsmall: max-width 768px; diff --git a/viscoll-app/sass/typography.scss b/viscoll-app/sass/typography.scss new file mode 100644 index 00000000..fdd67601 --- /dev/null +++ b/viscoll-app/sass/typography.scss @@ -0,0 +1,11 @@ +h1 { + font-size: 1.5em; + text-transform: uppercase; + font-weight: bold; + color: $black; +} +h2 { + font-weight: normal; + color: $black; + padding-top: 0.8em; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/editCollation/interactionActions.js b/viscoll-app/src/actions/editCollation/interactionActions.js new file mode 100644 index 00000000..56b94072 --- /dev/null +++ b/viscoll-app/src/actions/editCollation/interactionActions.js @@ -0,0 +1,305 @@ +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 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)) { + try {event.preventDefault()} + catch (e) {}; + + // 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 = Object.keys(objects[object.memberType+"s"]).indexOf(object.id) + const orderOfLastElement = Object.keys(objects[object.memberType+"s"]).indexOf(selectedObjects.lastSelected) + let indexes = [orderOfLastElement, orderOfCurrentElement]; + indexes.sort((a, b) => {return a-b}); + const currentSelected = [...selectedObjects.members]; + selectedObjects.members = Object.keys(objects[object.memberType+"s"]).slice(indexes[0], indexes[1]+1); + for (let id of currentSelected){ + if (!selectedObjects.members.includes(id)) + selectedObjects.members.push(id); + } + } + } + } + + if (selectedObjects.members.length === 0) { + selectedObjects.type = ""; + } else { + selectedObjects.type = object.memberType; + selectedObjects.lastSelected = object.id; + } + + return { + type: "TOGGLE_SELECTED_OBJECTS", + payload: selectedObjects + }; +} + + +export function toggleVisibility(memberType, attributeName, newValue) { + return { + type: "TOGGLE_VISIBILITY", + payload: { + memberType, + attributeName, + newValue + } + }; +} +/** + * 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 flashLeaves(request) { + let leavesToFlash = []; + for (let i = request.order; i < (request.order+(request.noOfLeafs)); i++) { + leavesToFlash.push(i); + } + return { + type: 'FLASH_LEAVES', + payload: leavesToFlash + }; +} + +export function flashGroups(request) { + let groupsToFlash = []; + for (let i = request.order; i < (request.order+request.noOfGroups); i++) { + groupsToFlash.push(i); + } + return { + type: 'FLASH_GROUPS', + payload: groupsToFlash + }; +} + + +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(currentQueries, queryIndex, fieldName, index, value) { + let newQueries = [...currentQueries]; + if (fieldName==="attribute") { + newQueries[queryIndex]["attributeIndex"] = index; + } + newQueries[queryIndex][fieldName] = value; + 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 = Object.keys(allObjects[type]); + } else if (select==="matching"){ + selectedObjects.members = Object.keys(allObjects[type]).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 + } + }; + +} + +export function toggleTacket(request) { + return { + type: 'TOGGLE_TACKET', + payload: request + }; +} diff --git a/viscoll-app/src/actions/editCollation/modificationActions.js b/viscoll-app/src/actions/editCollation/modificationActions.js new file mode 100644 index 00000000..6f554d5d --- /dev/null +++ b/viscoll-app/src/actions/editCollation/modificationActions.js @@ -0,0 +1,428 @@ +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 addLeafs(leaf, additional) { + return { + types: ['SHOW_LOADING','ADD_LEAF(S)_SUCCESS','ADD_LEAF(S)_FAILED'], + payload: { + request : { + url: `/leafs`, + method: 'post', + data: {leaf, additional}, + successMessage: "Successfully added the leaf(s)" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateLeaf(leafID, leaf) { + return { + types: ['NO_LOADING','UPDATE_LEAF_SUCCESS','UPDATE_LEAF_FAILED'], + payload: { + request : { + url: `/leafs/${leafID}`, + method: 'put', + data: {leaf}, + successMessage: "Successfully updated the leaf" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateLeafs(leafs, project_id) { + return { + types: ['NO_LOADING','UPDATE_LEAFS_SUCCESS','UPDATE_LEAFS_FAILED'], + payload: { + request : { + url: `/leafs`, + method: 'put', + data: {leafs, project_id}, + successMessage: "Successfully updated the leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function conjoinLeafs(leafs) { + return { + types: ['NO_LOADING','UPDATE_LEAFS_SUCCESS','UPDATE_LEAFS_FAILED'], + payload: { + request : { + url: `/leafs/conjoin`, + method: 'put', + data: {leafs}, + successMessage: "Successfully conjoined the leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteLeaf(leafID) { + return { + types: ['SHOW_LOADING','DELETE_LEAF_SUCCESS','DELETE_LEAF_FAILED'], + payload: { + request : { + url: `/leafs/${leafID}`, + method: 'delete', + successMessage: "Successfully deleted the leaf" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteLeafs(leafs) { + return { + types: ['SHOW_LOADING','DELETE_LEAFS_SUCCESS','DELETE_LEAFS_FAILED'], + payload: { + request : { + url: `/leafs`, + method: 'delete', + data: leafs, + successMessage: "Successfully deleted the leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function addGroups(group, additional) { + return { + types: ['SHOW_LOADING','ADD_GROUP(S)_SUCCESS','ADD_GROUP(S)_FAILED'], + payload: { + request : { + url: `/groups`, + method: 'post', + data: {group, additional}, + successMessage: "Successfully added the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateGroup(groupID, group) { + return { + types: ['NO_LOADING','UPDATE_GROUP_SUCCESS','UPDATE_GROUP_FAILED'], + payload: { + request : { + url: `/groups/${groupID}`, + method: 'put', + data: {group}, + successMessage: "Successfully updated the group" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateGroups(groups) { + return { + types: ['NO_LOADING','UPDATE_GROUPS_SUCCESS','UPDATE_GROUPS_FAILED'], + payload: { + request : { + url: `/groups`, + method: 'put', + data: {groups}, + successMessage: "Successfully updated the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteGroup(groupID) { + return { + types: ['SHOW_LOADING','DELETE_GROUP_SUCCESS','DELETE_GROUP_FAILED'], + payload: { + request : { + url: `/groups/${groupID}`, + method: 'delete', + successMessage: "Successfully deleted the group" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteGroups(groups, projectID) { + return { + types: ['SHOW_LOADING','DELETE_GROUPS_SUCCESS','DELETE_GROUPS_FAILED'], + payload: { + request : { + url: `/groups`, + method: 'delete', + data: {...groups, projectID}, + successMessage: "Successfully deleted the groups" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateSide(sideID, side) { + return { + types: ['NO_LOADING','UPDATE_SIDE_SUCCESS','UPDATE_SIDE_FAILED'], + payload: { + request : { + url: `/sides/${sideID}`, + method: 'put', + data: {side}, + successMessage: "Successfully updated the side" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function updateSides(sides) { + return { + types: ['NO_LOADING','UPDATE_SIDES_SUCCESS','UPDATE_SIDES_FAILED'], + payload: { + request : { + url: `/sides`, + method: 'put', + data: {sides}, + successMessage: "Successfully updated the sides" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function addNote(note) { + /** + note: { + "project_id": "5951303fc9bf3c7b9a573a3f", + "title": "some title for note", + "type": "Ink", + "description": "blue ink" + } + */ + return { + types: ['SHOW_LOADING','CREATE_NOTE_SUCCESS','CREATE_NOTE_FAILED'], + 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: ['SHOW_LOADING','UPDATE_NOTE_SUCCESS','UPDATE_NOTE_FAILED'], + payload: { + request : { + url: `/notes/${noteID}`, + method: 'put', + data: {note}, + successMessage: "Successfully updated the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function getNotes(projectID) { + return { + types: ['NO_LOADING','LOAD_NOTES_SUCCESS','LOAD_NOTES_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/notes`, + method: 'get', + successMessage: "Successfully loaded the notes" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteNote(noteID) { + return { + types: ['SHOW_LOADING','DELETE_NOTE_SUCCESS','DELETE_NOTE_FAILED'], + payload: { + request : { + url: `/notes/${noteID}`, + method: 'delete', + successMessage: "Successfully deleted the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function linkNote(noteID, objects) { + /** + objects: [ + { + "id": "5951303fc9bf3c7b9a573a3f", + "type": "Group" + }, + ] + */ + return { + types: ['NO_LOADING','LINK_NOTE_SUCCESS','LINK_NOTE_FAILED'], + payload: { + request : { + url: `/notes/${noteID}/link`, + method: 'put', + data: {objects}, + successMessage: "Successfully linked the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function unlinkNote(noteID, objects) { + /** + objects: [ + { + "id": "5951303fc9bf3c7b9a573a3f", + "type": "Group" + }, + ] + */ + return { + types: ['NO_LOADING','UNLINK_NOTE_SUCCESS','UNLINK_NOTE_FAILED'], + payload: { + request : { + url: `/notes/${noteID}/unlink`, + method: 'put', + data: {objects}, + successMessage: "Successfully unlinked the note" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function createNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink" + } + */ + return { + types: ['NO_LOADING','CREATE_NOTETYPE_SUCCESS','CREATE_NOTETYPE_FAILED'], + payload: { + request : { + url: `/notes/type`, + method: 'post', + data: {noteType}, + successMessage: "Successfully created the note type" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function updateNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink", + "old_type": "Inkss" + } + */ + return { + types: ['NO_LOADING','UPDATE_NOTETYPE_SUCCESS','UPDATE_NOTETYPE_FAILED'], + payload: { + request : { + url: `/notes/type`, + method: 'put', + data: {noteType}, + successMessage: "Successfully updated the note type" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function deleteNoteType(noteType) { + /** + "noteType": { + "project_id": "5951303fc9bf3c7b9a573a3f", + "type": "Ink" + } + */ + return { + types: ['NO_LOADING','DELETE_NOTETYPE_SUCCESS','DELETE_NOTETYPE_FAILED'], + payload: { + request : { + url: `/notes/type`, + method: 'delete', + data: {noteType}, + successMessage: "Successfully deleted the note type" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function mapSidesToImages(linkedSideIDs, images, unlinkedSideIDs) { + // linkedSideIDs = [{id, ...}, {id, ...}] + // images = [{manifestID: 123, label: "imageName", url: "http..."}] + // unlinkedSideIDs = [id, id, id] + let sides = []; + for (const index of linkedSideIDs.keys()) { + let side = {id: linkedSideIDs[index].id}; + side["attributes"] = { + image: {manifestID: images[index].manifestID, label: images[index].id, url: images[index].url} + } + sides.push(side); + } + for (const id of unlinkedSideIDs) { + let side = {id}; + side["attributes"] = { + image: {} + } + sides.push(side); + } + return { + types: ['SHOW_LOADING','MAP_SIDES_SUCCESS','MAP_SIDES_FAILED'], + payload: { + request : { + url: `/sides`, + method: 'put', + data: {sides}, + successMessage: "Successfully updated the sides" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/projectActions.js b/viscoll-app/src/actions/projectActions.js new file mode 100644 index 00000000..bdda5942 --- /dev/null +++ b/viscoll-app/src/actions/projectActions.js @@ -0,0 +1,191 @@ + +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: ['NO_LOADING','UPDATE_PROJECT_SUCCESS','UPDATE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'put', + data: {project}, + successMessage: "Successfully updated the project", + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +export function deleteProject(projectID) { + return { + types: ['SHOW_LOADING','DELETE_PROJECT_SUCCESS','DELETE_PROJECT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}`, + method: 'delete', + 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" + } + } + }; +} + + + +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 cloneProjectExport(projectID) { + return { + types: ['NO_LOADING','CLONE_PROJECT_EXPORT_SUCCESS','CLONE_PROJECT_EXPORT_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/export/json`, + method: 'get', + successMessage: "" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function cloneProjectImport(data) { + return { + types: ['SHOW_LOADING','CLONE_PROJECT_IMPORT_SUCCESS','CLONE_PROJECT_IMPORT_FAILED'], + payload: { + request : { + url: `/projects/import`, + method: 'put', + data: data, + 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" + } + } + }; +} + + +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: ['SHOW_LOADING','UPDATE_MANIFEST_SUCCESS','UPDATE_MANIFEST_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/manifests`, + method: 'put', + data: manifest, + successMessage: "You have successfully updated the manifest" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function deleteManifest(projectID, manifest) { + return { + types: ['SHOW_LOADING','DELETE_MANIFEST_SUCCESS','DELETE_MANIFEST_FAILED'], + payload: { + request : { + url: `/projects/${projectID}/manifests`, + method: 'delete', + data: manifest, + successMessage: "You have successfully deleted the manifest" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +export function cancelCreateManifest(){ + return {type: "CANCEL_CREATE_MANIFEST"} +} \ No newline at end of file diff --git a/viscoll-app/src/actions/userActions.js b/viscoll-app/src/actions/userActions.js new file mode 100644 index 00000000..d4d5ce01 --- /dev/null +++ b/viscoll-app/src/actions/userActions.js @@ -0,0 +1,152 @@ + +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) { + return { + types: ['NO_LOADING', 'SEND_FEEDBACK_SUCCESS', 'SEND_FEEDBACK_FAILED'], + payload: { + request: { + url: `/feedback`, + method: 'post', + data: {title, message}, + successMessage: "You have successfully sent a feedback!", + errorMessage: "Ooops! Something went wrong" + } + } + } +} diff --git a/viscoll-app/src/assets/blank_page.png b/viscoll-app/src/assets/blank_page.png new file mode 100644 index 0000000000000000000000000000000000000000..6958b3a694eadb6d13561428f280f765c92b7e9d GIT binary patch literal 13944 zcmeIZ`8(9_7e6jbmKF*vh*Xx*ltPMRt&nZZBD++GLJcZnG$JKs$-b+xjY8HaCQ2Bj zEJ;IBhO$JMFpT+};r03a3*YN|UGHC9uIF+;&wcK59%p;ZeU!Q3CLswSE-tQ3#zrU6 zTwL5(E-oHzf%R~u|8{0J7uRks2 zf7;na?5U#h^}$o@SDv)`L0Ve7QXyejCi^gSA@?oyQBA=ls}i zU20YAZ8f($MDlOzj2U!!RMfC)*~X`21lXiZx(7{N&CF+mO~baU7Fpb0*-wNmSc91w zt>t2@*o!s^+2n8P$*e0@GUZI2L1km^-=g`uRfBHUu#Mf{yPKY}3?-A->lF&y%sZPj zhm;FqqhAs}7m&$zw|`WBV`ogcmHsi<7QLA$w6#mwjG>b-nLGa~`B|l>fkSV0(dBg; zw(dsNHw@hPJkGGaZF{d2`PQ{eFtuGibXxsfkDup#k!`25LiolKrwLWT;VuC(C4b)H zy3|Bz5zt@~QTO=eN81ygK$+OZ?Qe z)Y`F{Kii%mH#GzbU=QsDp&0?YK95)AXc5L&cKx;xc09O!NJu`n@$UWP`#)a&NepSX zuP^6SN;0CzP%{*zhgfYrjDSsvt&MlD@3^naw|C3}|Gf8d)#F*U`CCOPxq|R{`{J`V z9O^{{v4=unarM-PL6(t4l!~A#T3Aid+r_}mehwBa>u9Ox$F*8mwfYHjNp}bo9IbK) zU!7DD!oM~Ddp&d0$T%Zl%#b*!)T%OZ{Z+|;mv@=fKU+O1qLB2(TF3Co>xB!49$cfu z)E14?o{3NUaf_e)b(HN@W!D;j{K{f%7@gIyKe+v@@|*1f*y&CG3}eVo6unxBiLG*Q zdFk1AQC_8m5K!{ATVZn;V_ADG3pV*93#2zg|(t=mRTT1Q(OKm1%0`qKIE&3cxS2(?{)Pa##|Q8aI3HllhGlBf+qjoEWegMEQr1M_O8zk zCme2Qv?Ei!=&=?t0{KOOuj@Cz>NrC)&0K*m$v9T`R3~z(J}}N@r=z1Ik|%Z_G2)^p z?0CTIz*K$PTed#l+!lPw4_2*?Ex$&=g*OwTfgo43xf|m4vLnDM(WQGmG$iOCHs3}Uuwa^+q z*r>9f&;6@cRW@1aR-v_6;!KGVZBl)Cc@dKoa*}cMxpx(pWei}~Pik(B}4tC09G0@9h+jFNO4FICc>n=GhwReD~csrxz{U6^Hc z>}AxCt>htF>oZ-AXFrCMXBVZ+^SZjD44&{Rod)@KxtU`jU2;#c0|ZN^p4GRNdB)7_ z@sPVB=kaRb?i0m^zcUxp6pgu)k0x=`$8h zm^EYcRo`$;Mw%&-^K`M6a2nwAl;vx|v+G4#KROj&JtkEuiJX?I3zRNGn#@y>x-uEO z`Ntf32MAHJuQyq{mW+7*=MPR4f*|fBd|uJnqwde~m6{^I-4-+2=i*&p zTwJU#J}8g+E7H&%I44UMMz=MwDDB`4;9FpmC|ta^+xb(L0=Sb~^6!0~GWaNKnm33g zRNJ?mJYXbK#7r)1cfEjF3Jp~{zkt8rIi(|#n|n>>$rUB1qNU`*Qlz))S@rTLi~Jw2 zc$MnfRR+~+ie81~iyizt{JheW1D4>J;A#L(6IBw)sKy%t*uV#ej%b%zF1n}4OGPzt zAjGw-dSIq~RuC5$uiKa?i1|q#*G%hhlWbG{>^I=~*`wBxcKJx`6Gi)iH+hSD7PsEY zegA2yKqIq50{`vh;N=QH0e=n@ zTAwgQ#*we9jYlnl|c}V+F=Y{BDI;nsVKRdb?et% zR#VBYkXzj1JcT`Hk^;cW!SXi-vsqP^$TG`bzneipo0@)pTB+r~dM-Rew^8!opTM*y zioVuupXqo(c<#na`-2!A>J!FU>dQ=FZ?2=P#`K2npA+-9o}P6r%sdqo6jZTsN4EFz zmQ>xwhacE~BOPr*a@`xo$1j7nTyc~F%EIG7ceXhEG%)oG4 zenkpmFY%r}H%CR6drA^mOqVX^(aGz_71%0rC>L-H1?Fiq4)3%!@X@M4F1iScK8V)K`j8Qp>K!Iy=5wZ zD7~L9>R-;-x&32%IL|sHWC#_!3&#I2bb~Bvr0^|AGI{G&8xrAdjocfy>vSAHQE4x2 z^0Vu&op|9LCOyjQH2D?=Iar6iFPg71^seP~bwP&Q@yw2YxS$5FsxogC=M@*#bA#O~ z`8mz8ePyzN|W>3NCh=hh9y2|NSyA3eueSg|9p2{jO*$6YuwYqkj-?}3U0~Yda*!xmk{D{$T&j@%Ob{y`U+qL!P>Mx zhANUwg~n<&QwXfBkpBuj|E(=uuTqgRIHtDhQd&} z?K;`C79I4^BPE(vf_|25bdN`TP)p2UyAeaxYPr=z|4NME{f*Vn$d7amy57&XhXz1Oe&ciHp;xqcVuysyI}_EFS&oJMo4Oww_p zhb&$?x!iN<_iuvX^Kg#b)iNhrSg6i4LF|U;*{_Q?)YE06nm($vi)MP*q%H52WBvYS zkHfiq_RlRZfhX<_-_#=bt9?ECQX^9+z(Upq-Kk~B_v_a!YHgNu_EQiT>$MNltS`X3~D5M=su8W~$~-!L5wV z;RXp0|76Z7u}_IGvjfZ+ z865xPS>tt$zS}v#KZphfLWnY~Yr#!th-E)YYpR?Pz*uQ(Ktskr+5a zjUy2Lw+-HgGnkv1iETe_s##Oi>YCtDVXI-JvWN2f{;&$(nmN&Z0ydJieBpi@`tL^D zhWo>%_OT5U*kYZLI(`hxn8bAct z(gT(})Ub=Sr`L`ub{MZ5??SmIfZf}gQ)cWr)eW}CF(H#!gDfH0%sRI-+&#_sn+4-F z=7iOad&2?8mV2f)W+{f(xp~;W-V*PUd`jVQ>jc&UY!eP)pGLV&0xLtA^)2W${!2+} z>!m5qj>XWf2ynmi34O>OA3^M1GTCFy@MewF>44xLnqgpgHW5<)(DaY|X2w|(#+$pI zJM_9ZO_{ISyA1@Ql;w>+2NTa&Gs3$6ze*JV#l`}2}x zh7aj?I8&F)(v~Fy{QVDWx_-uAdW-+{Aoq!%r198ONKJU}-4H_)3ISkSbVkVTDZ`XL zDJzrQJ*bDf!)c#Q=yi40M)g*Vi+$_6qVD`^0K&nckCmP?*=Mo>HWSBM zBAys&qmt8^-q2+56PkkRAL;6ClhMtC+e|~{?na%k8B_M^^|SdFxw0#-t7U3zS6E!~ zQz_ShcJzr1UB<-%O1XxbR@AU&SQTjW5q^iFM6*_g)kF(g7+YZRP$y;8HdPG^syqwk z%`hVQ8Hx-RMkDe^hTf_wlT*9h1~G$=RQQNv_tBd%vZTT9T94OWQqb7A12gsXy*%oK zRo4pUEiZ_2mYd6D{_@BMV!fzA-D2g|7U%?*PRSMC+fAC%Q7zkDSVFL`2XSuE7@cFBw~f)k#(d-^ z{ERnX%*bv=YY16Ds`KtX%Gg18U=fYaIuk@vS6JIG7VWi!{!{jTAL@i2RoRXDv|Iz9-Vm)% zHI=1TD$}(ugUF(!WqfXbep~#S?keMGb#Y724hv$1t;9~1wr(jHY*KLpFHAGfE0DPW zDS@jgqAD0MZ+Bs9zBH0f%A67K6#Ovi(>XsB7_Lu^M9|d_(d#ZlHgYE?&~^{~{YAu@ zZ@(b*uV>!J<> zP2gVBt()=Uag%Cc$vdA%h-k0Z80q*N-PJ?#zZ)m~E2-z%}X7&BbT72J~7G-+s5VP;w(EcqaTu7~y_VbZ^SfQ@jl zZZB!sET=#5MCxGw9a6t;o~A@A@o_M~zvzDY6oOti(Z@^XH$KvH!y(c4XAI9O(8-^s zPM#+R_3c<5SwK-SG>x^$dH#~5zeQF+G!_#?ep({6W`l$A9<(G6C-wD|YJOwCfj&j# zfi+9JLHeqm`!(^QUe*tTf=3Pb+~^rQA;j#ig35>7J@f8*())VeAL}{2*KO_bvpG!G z!umSpcCkZ5tWyJ4@?n3qxik1uT|=x4)S`h#;i(5#rCVQT0!wn^AP1L*w9mP$&y=;J z&7SP}bSHUAG#}PYJk)dgPH=k1=+$cbTtcKx-_$&OS=MtetRuV!_2MEFjDTH=q&7pe zrzB>w^?g>w&FIyyZn_K5Y9i~R9qU2+6~xvi3JJ&_Tbw^Ggy6}PL#;cyChXLXo6}Hh zhO0>|uBqlbMF1;&ejqt+?VzT&5QP>FtV%oL7@@i#&tJzWg3(uy3lghFv~xl9Irp^T z>S`M~ec-_6)kw|5sk&pf99s)G@{Q`9Z+Err^2a#niWBHO(-M(OSO*sNohnZ?KE+WBY`nP&a~pw_iL;6rV;e@ z^f%G`1z5QQo;8xCm%FdphKMS57(!lE+Oi4w+*8(@!BLaoXe>|shualV3ZYSP%a5cO#0|9-admvYlC~QyaV|NRr5pY*^U5ss(cs zvgZYkf&M3$pV<4^Rej(!|2xp=5y`_uY@RV@SJ>hI-Z21ybQwW)WBkU8a@`A}5PYlb z^GX5{Y#PTw>}tFlo6!V8jF;9;?bWY10`i@_lR0HF|9po{uV2gc9R=o|s?uxsim*I5 za?ls+&cOZhv16_If$O6nY@!&C^r@|J9$6~K#TDAC&hG*DEl`(^oC>H_A}w?7J^sHg z4Danh)dyo%wc=vD=D#K>-i;>#2;d!4!&N=nC1BabYCM@H=FTOaDaYGyMk zaW2Ud6ibd6bFsp4Gp&lKzn50EsTGV+ZqMxxf2gOtsEeRPB989itOr@j=mC0tPQ6*0 z?_i`LNh%-hbutMM7x19tEcs4r(Rl2ix9_tOIRM&>mE1D27Ia4uJ(Y)g>NvsSP7V{D zlKrD^)eig}(b`#$JqC~Ds=;zO0b;KeBQ6@cwgKvUgZDZDS_j?u8<_JSiZ^Jj$sGu| zpR2j^kUVj$o>%GK(^V**jmAqR;RlawgNtYa!ZX?hxwe(sINF=IrJ@($VGue?Tg4HB zB5liSEBT_m*yul1YvJtjCq&f8q)g}&zxXo56iH(fU#|Fl61GJO)I zpcn$R1VUIo$SG>*L10K+7lHavXqL1p$xoB&Nx%_rw=_IIa}P;Dg&*IQsm=| zEvD5S5||qA=1_D-wvg;(jKu6(nE7esUwB7?5VaR#Yp=6bM1 zx6_wyNvVW2BVMZO=8r6pNlOVShD0wj=SM{`vDnH9;yR{7jTX9SQ20IOlOdB07TK|DW3vxLO=gw1B!qOd7AiX>f|3W$ElG zYW4x2+GZapDe}iP7k~G3=QZ0@LN5(V$kSOI)eOu2FW3EXS;BZsdD^2w<%dfp6k*-l z(Utb>VyUl(%PKndq8{!GM_83%+id<{%{0ZO0>D00w4_8 z=8ECLTY5f_=0h4!@}vzXvheQF*6OQZ^WlPS(p$ne1XLYV=Znxp$|LYDSb&+(9gQ@? zEBA+MUxG5{9{$oBsA>tMRuxF2tUiRr^^W^(V+W5yNrGSuGpf1GB|4vWOM@68jS z=E}C;fEfMgTH%>`imfUK1(rsn2-M5x%bR9EA8~fFbSbaX!m?3NCk52T&}Y!2B{D=c z@Vq#TEZzPq*`Arfk*U?CPdV{~APJgB&^H@K8EGV*$ka$v$>cfljTgTIA$BSz@PeuB z!Ndpp)F~*CNjy!iWi96^)|-Pqtd@v$`iGnsd!S(_oD-0)Z+@rFo|#r4$f+IsiWMWS z{8NYq2p>Gd&qWdSBk5C322I)Go;24o=3xcyo{ht5vH4`B2h=~L7C1CHF?12xRTR~+&!kl*L&MxDBI?0wE$&W_=uHJHL~@YYo30ycjE`lemK zEsYkRSA#A+zkgPH7_F1-|C$jhGLxD^^;F831Cvlo92djW?n#IA6~NJ{#?r)vWM7^p z(jmTG8q=qqvb@+^`6W{O1kvL(bQQcpU)VnH&-(BKqQoVtJ{1OY>aCA1RNEiaXT6&V z__=L&m9Dsc#zPtSuK%S7eQNcG2ArjjIUzqCR1K2YvS8RbrZR0HbgL`&t_fvNe& zQ-cyTMnL-*s>>cEi2JE{13DKB!>wzT^iG#;0j~yJZSKR+){G^i!`aWi^+iy}c?1p-3yFuazzAiwn zDLzs|G~ab1pw{&`v)PZis>E{Iq%=NV|ARuP&%ov5ZmT^+ZbWM*K`9m>_Tye5M3i5^ z)kk>fAtF1DUem*8%iP-sZBYV&Y*ADuPF7 zOr;9dPjsizm#hw&{&^ZpCYNUXP~hbGL6FsZRkdCp#0oHp)uQ>^_Kn(*MRE)5g*%>r zz=o?BR5@?Pz)$gvm_xre*S*yHw#>C|iIE>yk0o8ZIeNn+pvbc2mhvPT8qPk@n)~+g ztG7|Rzc8P>hICW}?VG<7$0+l*TV=c|8gDZ{aNE({`Dun86ZHfgkpbvHhwUM2+jLEo*1wumEmVPy}$EX!WgSHM0xR zuTcT%ColQ3<*+fa;|z~Qg}pFP==`~_A%EmRb_BGY-S|-|-!;=*x@h~}Z5s0y#F0)y zQr>s3E-)Y^)YrkSW?>gR2Z&en^Y10y*H;6Q-@%x`glB3ln;I{KOn)5g-szR6mvtsO z<^w7?C!|%!1`3X@^Dsx@#)TkREJsVRAv*n3vcxuMllfY|Hw)K|&mU)y^GCervd`$b zaD0(o)}6SGsN_uTT<#-KgkKGKjWyj?6hbH`#)rSbr&G&TU5AV$@{#|T^D!nTQs3u`^V>{2a5bsP+d4?=1g1kd=E^;v(tRWi;P`=h;{S81gShQq+( z;Gff-PftJBxK|L}f$Oa}k-MBA9c!T>wV7kX`2mOOBMcF(Dp%Ac^~UapBD9~}7{)Mf zS%L8l{h%t`lz9ecc)}_$V)v0pD|2P*goF2>{<_a+QxVytFNLjwc5bMT7_@nARmHj) z((S`IH5`V}h9~4$jhXsAJ`DbKz(IlB5xmlcC?LD+6V>fn_F`E(wys^ZWQek`XR)-7 zxVSi*7xPyD*bgvJU*i(=j`tX8sLrpW>AGrp>5c9O%0Z4NZJNnc!ajrmR06COn6+~# zTnD!wRa5v-`pp++?J%2b5X%G(gr|Ow+8?+5|BvOhDR>Wa{}~vG^6VDflDn{(7@?@& zQ*Kp=gUP@1D%;C@h#LF7RP5BX4VM2-o0%m2cokTEQj|QBJ$RX+ZGYGaCT>`qi?O$S zDB`>TRx1>BMCSie()@k_QLSjKM&0Mg3xYflFY4kKuckz;(l^4D&MLJvEnV%IswR)y zHXV*7Sab)}Zie~&y%bsJ2tyH|fy`su&*~iSocaV4GMMOnRD0N0$gdQ!2gnq(sju&6 z{6%wbiV-8i$0hT@!O~kIfhkf+o1CPK*VKg-LJ$0zMU60um5DUyQX2xs!#6+ahQtm` zf4I`FHkCP5rJ!bylo7KxDj{q`n*?v}IK8(jjIc*x2C5B_Pdz;>rSiNznzTiv7k3h z%$J(raOU*fO7Ldc<=M@^ z&tLOQ`?6f#Hi%si$xk{t__4?D1mh`}pvu(?SAY)nJMRY!&bp(x_YDVXzbbkBsn4Ks z7-sCAWtwkp^aXy1<5hA6+Fxj;r>7Nv-6=EeT-TPUzF+fNU(WIchOy9XMBT<`b5BhZ zr`BaUQ^b6nTcT|6f{N2ymU5CLi4lE(e6X46nVFe_)Jmcj5FY_<vQd8ps_(SFc_Q>)V=d9Z3+p3h+vv8Ph+A{QH78KyF)(6L&u} z{9X;DJP!$Fly#92CA{3?4>+m^PJ{EVtvxWHmD=o@W4$r8)8<*-o6=3=(FwDQ6Hm;y zH34t!hGx1~8P3H$lFOt=t^IkW0`lRv@O^4#qAM#)-etHFU7#+x=7b^=s2jzNf4dbz zn6Jf^aG6iX2oWQO;W#gJJh$L~P^ zTEUSa(!$M}gtFjG<<4bk5+7ezt_0(c=t>qTb6KNCH|LnT+IDff>{q6V+(g@=xy9RbgY`=kBYmPS*D_QINQ&%V%BcrR+7NZ-d*A?ney)4J_2vx< zD82JvD>r%4zHxtMZM0%vxU;mtlzY8mp^A#$25jbe!utK8)AIHV9j(*7e(?JX+KCIS ziN{=gK+)N45M&r!ENsP!%}##C{yfOzCzEz%ces`yR##wXUbav%!mc@h{TTCQEYWi5 zqc59hwcDIUIXQEKiDJCUsvy1T0R~%gkyFa$l#zD!Mot_2P1T&<&!=)lE1}LZB`b5H z`Zs4sGaIZe#V7+0y4fAftG?{{70T57VMg%wgxiK&@^9z;=6Ud)oAiP2$p3!$@xPzA z{O>nE|Nr@QlM5%5@Nm6(63=0uZ{*^7U^O|v#34U>RHQI%pke%*y{-^5@>k>Y92tI5 P#$|lU{A7{-xsd+@MV0teG{$ldbw(v%f%?jv0G#aVWwg{pi?qp)_W3IPb(ZStS!p_m% zo+NSD)dN~1h_c#Y4?BlLBp;kT>41})3g77MYCfEkqY9syoSu}P$1c)AC+#C%q&-LU zO&pFKa@gU>r>4rRd{_|%a3%TJ;SRgHxOpocR^g+Ls|e5N&ysvNY7?JBDtzkbggA3O zL)P?VJP_xG3Z-zMSibwE;j$BrG6QZkYYW`AV0<-kh~qdoIHG-+}&{KxOVpLzCJ2^eCR|!|9sz^El}4*X!!~uTy*bX!rpH-y!|ip57(_9wbR)lDE6Bmjg+|54OVpZ@c$#+W$*9 ze;WyX^Pg)c9d`PUZs?n(Zd6rRl8`d&RdJUW${HxySATpIF1gA``x`c0o^cHi&gZ|6l)KL9%Me`f7V=`9FU9HSzSQ6XnBJ_ za4kpUx6gmz`YlAu8~lN5IU2ux{sY%{jw7kI|xR#^w+vh)U{T8C-4gSEj9F5;T|AFhb5G`-;2d?F4 z{Py_|T)%~Ad4oT2El1d1F|G@QIh?Y0_1J`mie*63fuHQnmyuly1mZS08=Ra`$7NX@1{=l^yjo&{1 zf$O&rEpPA#uH|U__W2K7zlCUdgFkRBN8`88f8hEpM9UlefonM$zkU8+;bQ*v4Is%4 zKK%2CFZ^~`78=0Ufj9?kV?6{pB8VX5V+b<62!9VCh_4ia4B8@y;wc2-bf?(VY9h#a zLn1-lm#Q}C(edpcVg9y^#`?falamKS40= z(QdaX$1_{v1FxTtw2bL|{qXdIN7r`=#jg@OO?*;l6#DSdI%O$b9vA&)?3ryhxQ?GV zku~tVZC~jpwS4CEvNJ{Ag*CIjR)4*b&HZHHySH%W^Xx+KiM{|@SfymC$>-6x!UL=M z7!U$6h7mR+CT8Xslj`GpHNI(heD>jNx3`||OlEr%3vplPP+!x;k?NE)>kzCg!xs3e z5{XLPqEJN%QqpT-B6=V|a;TEvhe83%(2H^dbPo)sQoBRUA4ChTD|FxF{FP!H}pLTX4b zyRJ4myAC0jWMXI##Aq9_<-FLQ!%s|ZXqx3;)-~R>e&O7%!4UdKPls6$w7Q7M~(TlgQw&cwZ%2wI7Jrgrb% z-YPg>nX^SOR$z_Im!7x0!EJ$ULQ`dXVqPKmM#C@=(Y`|zpQN1XRG-u4BkvBBf{|QM{DPIRrYn-^h(epWPee8B zvZb+#^f!GsHOhA|pRrK86WMw1^@?7f7VVL|*XrMPY19&l*h+oFbUme`r^hRcQ9xdG zb$L~`{4L(02YIW>3EAl0dMQfJdl`yvIT;2txCA$R)4j3UXJWK~K!@uW0o}lhdi6+n zKkzJ5_cw3xu*YvY!aLt73QNK;qS%XQM$6Q$=#jYBr&AI^L!xxXJN6-JNaiJK->P(h zc^2ZLhVe$vaN59-%vWBsiC6Y4@^42BjnEC*N+k)1C;Oa!ImEy92qBR^WKhL9DUdpR zG7~yHSG^0dD@pd8$j+K1ACoH!cqw#o`qIvHI0Gso+cmB47g%P@gn0}FPK+wTi%cpU zIFhDy)s}$*`wxOH%0yA(t_WJz_No3Zv+KMeDF?~@sQ%$#IYAx`x(dZ(q6yU;gCW;n z_!8*CsXEmxYt_P{G0kdvPQWsz8tH985oJMWP$R(KxS>6Bpt!^7)W%fV3@&p1pc1Tl zR;(FW*onTDH4F|vzvU4h!`-y+V3E^NHNw3XQOgQ_&Wrb$RGIP5pv*m&b%^T-ZBSV%NIaU_$7Yfm2)|G+4JFEmtph8$oUp zHdLw$FZc%fD4J{<0z4>y2!2b6yUoZXYA=}tRIK0DW=l4|M>Puh(gIRD3&tLh1rXj1 z`IIsdBt65%F+5L$^gf4ozKQ`K7+6uNAcyQW-ajpwTSeFvuke-&ao$|c`>3Gf{8Ugh z7FG4TE0mioNn8&(1XyFwShg4y&(I-rd82uHUFD2a6LhZk%usSDez};MjEmLoUzb~j z;1ebR)Om9-LfTLiGj0!_+z#2sT)oyi$+CR+l9DO1;UWmGZD!N_tn=5Nt8B4qsKFbO ziiVz+ZXH_OqE8ozLWF#nP@O4<9hz)JA=vv~LH|0*;_>Plg_Gzoj?rVB2tF7@Kg`p; zqJfM~CFWt}A4;;gPUMqWqsy(o3L(oh059p!yT6W7rcR>X@tn7M+p1&Fk>>6wX<-EK zIYU{Dyknix9klt` zl!;{`cpxB+w47Rs+vQ$iJ1->!Nl(#eE(LOHOpG0t6Qc*-R;UkpmJU=BTUqT{d+(>bi7ZDKl;Xr{mR}c;y8|u?p zKZ1fer61be%Cn0*v74wvhhW1J5CDy}c}d%KCaeanL%@7e!FCj~781}=#N-Ma#ogGnE%@D>aJEov@&QoJM~ z_cJ*K?8V=Am-$a+%sG+;9{QjaWu0z?U_Q+?bbks!0SIj~1xGXSpRkG*6W~Bb()!bf9aEFlb8Di_HU@h@4`yb#K>&0ghz%i>(9O z^;$DD*%F%tFNdO%cmzO4e#y2$*YPb5toR63oiz=9-nwV!jBC&s z%TOEK_srP;xI})#iF+ah*_)KD_8>NaHijd`|GVi5jPON$vEyMt45j0G zo_tnrx&YR9ZrG9;TwYW=igKG6a}y2-p8HQ*od z7CYj+b8}=tqCN+@jL}WrQK7h{`Xq_q#iCHDm)K2e<^1GIL5rN-)1hLv=+YcPqf$N? zkqMe6K&KB=h--DRc(K1mY7mI_FXB3BRM>7r7UGcv5$9;M$(+sV17;yWe-G`b zQp#@^I4jAFwLuqYYY6aM+d8CCBXwAvXSE7Nqymk7^*zFmIN~iQ#Ah>ffXU@M^3$9ILC{@;`rHfTV3SB2({eS8s z+d+T=yLm~MfGOpj2w`V}lrpFSJDD@8Q(?#xxZyDP8$2QBKkS!Z7FD;+X;$;^Ko~!b zcPPOpJ_n4KO$^MNjMdF;wFK)Yuz@2`6hrM!r&PP8oG{I*Lw)bX)2B~#Vos>f=;(&dROd>19iZ%fm$|h0<&j3yHb7^ zYIDiPR#588pwU``EHtSmv@NY=Y$?gU&PYzqtW!jb>mooKT+ zYXAx~N&2}qQhMX#V00%1R|`WVqVpv^$FlaaWMZAdDg+bW1K-v+d5hrrvV~`tG{RTA zLaQYu3kgMG3{ChM>}aOtFi?r%vA`R_29}BYV7H&8!RJmWH(dm?&;IVU-Q#o42wDj< zV<+yREiV5wd#0rn4)}?F)--6nmp2Cm^2c zv(gC1i}E!ucwV=y>pJym*e?Ga?=V_aFVR4DiQ|m4!1?Pa3Vo*ms`u4q7hwm;+XkOs z8LsphMc|Ku%iIhH>EIJ!l?c3)0MD$~{2QMD1M@J=k{QWPy_??c#x2<3lq z;k2b_2B)E&+bVhL(sTeNA_mF}Yf&E`px07J@nfZTM%hk;{NIYH9*gee%dar}ADa0M z1IA&C{po$RmW4aI75ePB&#ZvuT~Q#ALWoBBBDH^-Fg2chfT*F-+C&XN)o^7Y6NxKYuo7g(e&d2EGSRsCzdZ}{w~eTgKK@U@Y0+?ouj!w9N#&wu@qx`m7B>1YsMX+a z*!+9!{l1M8gYA(*_#Sy?tUuSc146TR`@eh7HN+|H3PW}rqX_IZBEv^<{*qIAt2UoZ zg@7Cuh1)`CK*A^(*9~felkq}n<`)I@nuh!Z1{{vS1_(P9&%Sj||0qBK=Lyk6O$4Ns zOI`)H8|~coE3W+MRYnqhu8ZKYJHaxsI&nR+XD;1(DBCEF`UGkjhZ>`NH{UDhAU^*Q zbsLd!AJF%*=JTvOpzvUhO- zCHkC&E!v@56uGhxks(1`{Q!zXMDuwXM^I2a?w74D3u)FBsF8bMM|Y?GBit6UNAyuE zs_L!y^cQgx8WH_6b)f+F^- zoSX#;CHEsihrKF^UiznNc**q%6m>Wfx_W05eVvni$TS z)fC;vt&4We1(L6!4q>uE5DjfIap0|;A1*0hDydN@R@>vc`^hDbkGk8HwZ-N5dqA9q zYyXQDktlSXdwUh0o?X)5=A`e*<5!XEkp97T1yMQX6PG#c@0nY&V`9#k2pSOH39+M!RLH?H|6y(k}f9CR=(=WNGR zJ8^J$cHz#B_9`7Oyb~yDvcOsWYNzy%vcE{PHjp&3skG=-kz1!|?^TQDp?S0G?n)`5 zdIFcd-)0xkIMCpGKULK6yDy6>_j$gi@{XVX-hNT5 zZJW`uC~r^2kFv%!D1MRIDsPj{pR<1Pa4_C>pps@-aq{~5tsCy1U;!QM`5ABV9OqtL zBoL>-fMjCcsKl)+b8DF?3kqiqzSW$eUrgg-3rnd-^VHm}k7t^G5lht6%n6o>i7#cD zJb}@J0j?mUs{#xmusyz~U`DB=%7a`CTWGT`Xt8Yxf<4{~-BkvI8+U#>79RTctM<%0 z&<9l=_5}c&VE&7@(9$11(%Ry-R$)Qe^%=U#jVU({!r<zC~jxB_r0TVp z;`qLorv%YD*BhW8h?d^Fb_5w3NOom?Wl0JT1x|bna{elm#V7aNGDUYEzb0F9Gl7XVJO*Ovt&h|7*bq3z z3h;lds?t#r>EPbH0p0_a;bA#=(%TmEp*)&4d{B8~T)%#LV?gQ>D1&|xu7BrBE;^SKzR3)F-V z-Gj3Az*hwr__nq6?UPao*TG4~RN+B+k3x_`k2-+JgSL|^pkNk=B3yYT_Bk&sk)9W- zncrfT1%l_f9RY1g@ZaqENn7rTj=&s9>~jKbo1FR_-}0?q!|ivn5-S z3#8pm_cN8=+5!V{yDBWhG7sJqk0+ zfulT|N}69>T_Z*OA2`GM)LGHdK=MmjBZ{=e51K+>Gr?;yFDw5dl0{d_1B*Op5b&~O zHuIcy)$!-N2{{tP==DMBqG+q7a`{hGQHA{pDvr28aDD`gyoirEIgAaHcMSwEtIfx) z+>~T@?b!;nS_T$|(rHhE9#?7AL7l4i@o71| z07~FmqfAtRnSWH#Y2k=hZPcwJSJ-kppe&$iW2g=`w;nAqmR#Y4NCwqO@DnPPcR-ft zJ~;p^5{PN30uHlUcWH>$s8?|I@Gi9azLgU9z1`-q0|X%wj}D3Y_UJ{AXJif-j8^q%L3QOVrL6AYp$SB z8<;Y;`-k_CrFxGRs`qet^Psr=utCdW29SmgEBzb<@%<ffgs&#x@IHK z!0R&cz53<382NOtgg7b<%@pk{nc?^6)z59M>-sux-p!kLghSxgmz=3Ok=+QAcV!vZ z^H+e76|5}{!*F81yhzwYK%c4ohu{x7Yd+oPJ&=AWCn=ipmKPZ4IMCovff0b^8CS!+ zJ3$|Vf){uKIe7+r6lDt(<^;@6teRMdAQ^WDS+obK*2K702OFOLQ(8|Y&+i%K*qFRg z^@!Ayxb{^O5G3UF|BTl7WBLavH;$sdmXA67Q^k2Uj2%So=t)WY7w>*cKaY@=pO5t- zy^ICg2x9l;Q)eE<%AC07`A;5{fBO$l_-`!)5 zKWGPM`dG&mYdZG5tRTAHW?|Fxk)8?D&cvwaeJ_BS#)fPuZ>aILk{7GZ+}-L39lCX| zUU`%KVsEF#JS$K%&KN|@{M>xL`Syfu%8G^={F7h-IJX0?7I01SS&=On(N?-$kTFbR zsiJLseBo2i#T8Q=ZQ2L;62D!^Nu)@8>KkF?qea-A2l6+fzD1p2_RB)sKrGiF4cxXa zT1Dux%i`Wo0bpz@s_$vP-JOjjynmt|+$1-DXQSxAo(C(C{z3>o(Cv)y3Ni6ltRv36 z0K)sk$4oGaVZ_QVrHexAqGLtty3#C1-c?AvyC0w0Hx_6;GG11ms2I>&|G--9sgN}i zaRNwA*UYW`@u4X!3JfiHOd5#jUYl?Avsu5e>FZa#p^Mm9lm8%L$CsmO)vCo!@zJ$; ztC8@AWnPa}4`Pc-*aOz9KyB+|u_Kcwhbp{yPD*oP8ZR*~&MRG4WAG}LxmYjPalY@Z zb)y~~vhWb}Wgn!_nl|LMFV|wK`j%wQIekD>73-5L$i`Id)~RMZ@BTv3cvy=Tk(>M_ z`Sw7{LLY2-AxeatWE6I{{1h1m+g*~4M@y{Ci4!@SVeW|@O5xr6}cfX+_spt<_4*DP@h^jIkLN5}#~e1>6} zAHVCz{`8OJK;FaWgzG#%R7ixV|8-Clsx+v-7@no-c>!@ES*Z0t?;u70)$7@G#1rAOrx(Lo~V88vDN9L^P#ogh351At82CnynJWG&vtsJqvOXB3x^Yz zt0eZEYHJ@0S&1}UU6#I3m6b$b3d?^~xX?fAscB7K0Bt+QuH)bSx-T6DJJK4n-L$9c zap9)ZNButf^Z*_@6d{9y?;j$D*Zj+eTU#H$f^rI~^c;Amu&7@ZlF0>y+&f5a+i7iq z!_WW9dYdg7EKSn(8D5*^lKj{%5a~5P2L(iBX0{k>C-N0h&F)GX! zxh?X7LN9mFba|bb&H(QsD-Cu$3Dkp+r|z_!#S1iA zs8oHpKNr6sFUS^Yz_!aSUuDMFHOMUf!&iPFL&lRo!w8oZ9O@96J5JE&Df@Zs>Oc?l zAa@_@?Mvpx8@*stx2qbvOW}OK>qLm^6B`HcWkl}J^exFQ>t&{gg*08si36rb^Ef8Adi83D2om9<()v=G zxeGtjsI16l&P)cblj;`V^Cyak8d+v~A7A3(3@!#gR5W@8Zn322v72LCdYKae6rTB& zCQpCFI}w|}PPnr$>ck&B`pCn|?6OIeA^FR<>jb`LomKO*59xHRuf5*|VF5=UT78(> zRGO$T!zrS%6b-eGv2G{S(f6HOc+064_^CQxrP3}f$G0+gU~dae{xLQT)B(_&8ffVG z?gnQjpNRTQ;)WpURgd{%qm;Hf9agO_hPxkbYHR>b((A$J++k%%FnfTFvkbdA|mVAvczXe-?3s0kLxySOY6k4TuQIJLhqBkQpQw=S3PhARpK^U|5`MbxD-(F$!} zK*+Gdnl53yuSOZhZsMY|GO>>RGpztGs{U~cq&9|>FF(^w*6|Lf1DAUnyyui>-kTZd z%v@D8q97b~(UwOc*V?wK#{7N7CqvMx^oYNh?!(5Wk4#*AHKR0>X0(C5=k8`pG>LGX zQokRO@Mvwi?4gg2XLnpmlw!Vaefmq!-Yk5nQCPP?(U9AOvU`4y!rb<)hDwFt%bT*k z)CTti>XysQ7;}X3qV}z|DC2xAbgNbAE=b`U2jfLlRHbH&uk50HN}CWd$}|;G#}U!< zv>K^kxZ|_2Yc0M}1t{hd<1FVih_WfR_T<~Te%)G){jfl<a2^-`uVh>zXGM&kZ*a&yU3z5e zo5Rm{gd3gO)o^9*aF_@d?$&K~?EK14sSC> z#oR)c7dbm*Ue&pEp_x_XI$GIa^=kgmoIZI=l)IME{K5he&PL7>LNl{4Oo_uEa|;CY z1Xts3h8c#vmYce@zct^|!NS+!X06!b=b|V5E3t>+9_wUHUgLNEcgu*dow#@qZWqrN z$BAHll5uvoc0P3puF!^TmbPhBuzvd{^Dki>yfXoAmr-#gCRwVq1h#4BKWVN;NwoEG zdER`Dz&))8zMqT+m=G^LK#ljC;Dhicq);%7^j^Nv!LBgxvapMWvA1-0?g3$)>B=XDaUs$Dy* zzV)rt8Ow|BfxqoXSu*XP^0Wf`hQB^cy1sw+namkNjMSI9{*`>8Yy#wx-0Gtz1;~av z5LTqep8-Ut*B5_##F^g{kExyGu`mmnv4r*-@i~fMmxy4&)o7;|Pedz*vx>uwxS+Ic6!y*3 zZ!zAKn3lvq_L`lci|$+F$`ityEeT#<@zQP@+NCXYT--f9rfh^H)v@HT}HaiI$A;Q_1F;;mzy&;oLi0#;G^P~MirZJTYTb?Af2|95ZA@~7_+a6cI zs{vu!aRt#Z+~bcjcv%*7HRmLe7Jmy&eXmx^jxXr#0v(@^!Y2l{oIjH#x;CB4nn*w< zW{jq@%x3L|Oj?m__oG)Q8^0wybP_P~EE3Q?4Sfu0HEp6E_OCQVbP~9;5gJ2%bQynb zFY2O$#tGkr>*{p-5Z^TNp54G$AEdwDAa1<`Alf=aH0o#myw zNmQ0cAc!|EEI0@nN%|bSBJ~cIS7vS;-FJ=y^(;@P2H1C<==B)~5JIoR;d5Hd9Gb9d zLCNwhaQftf(-QzoQ{NPR1DmcCch z=KYwarj6LsBu8zylww%Yswn`zpFPOA;o$xHuFa%})}Mr#GrdIA#o%%tUKAMcQV%^` zvm$J_?zpnPRVYvOo^C9y)C{c&Q;!tKPFRomV(*IT=|h?}`EW0BJwxvbB)R4Tliqj# zx)fL^FlU0~V&Zm{eN>q*TO$r-Ye5O;eqkc~mZVH||lmgmxLxOmAP z)|}=H#Em5D23hR}&)Q7lj1`QT1B?5{xJA8z413!QdlN$WAgxJXNmtzL9N>-W?@_q% zC0OEmTcd@If=Hbzv)XuM_?DiRN|KaVQ^W%@DEzgfVvFo`)qKMTn^#+Jj`S(c`quEU0($WWfyNn>nrI)#@?a#4S` zLuQhu%?nm~tV%M@q9e(6pEhm8Ih(nU{zndts~7WK;HE-!Yph{^<0*OdNhXPbaUX36 z+nHdNXchGbL@B&U>B!(SHMQH3w}Uw{p!rX+;K7@_@wd(2VW!Q>?nx*NFsLp(M%3wU zaQI;^OB98|uE2DtFK0EJ`SbDL$(B5)EIB_Gf63~iN^W;`V}fXF)YRgk6Dd=DD7p2H zeNRWYf*ygZLS{Gyw3>%C^LnP`k^7?`nmHG%rKXh4D9L_%z9froZ_#gxjL+Q|;8e$X zzJNoin5Xl`pyNKz05_cz>yzvp75CS~@P_ar3DwKpDH;%vY0%t=-vp;vwLQewZ=zOB z^1e!wN|jEHH(a6IQza(P{7%uYdX(is$zWWy`@=!i1<#sfr-#!I>9I~=8I9>XG}5eO zQ>m&)R@d+{z{>BW&w6(}#jSLL9h&9FfovSOxxIK7GfT`g;!X$XuAdDGG6)l_80fbKbp*M_darNOZ$k? zs{x}|dZK4lAFei{+kdrqLyh8Cuvzx{w1v>hY^Y~a0#mS^#oqwEO1lC z+v6vPzGA$>1+(6UyuZ-dG&~>b9=v$*P|0Xqvd2`UZMXJ~+rL{WdTCEt z1m{>QKVB>|78foiH}gKgDZojqs8zgu!0x!#HSy8eP^h~Em9`t4S9{L8@Nvo}UBt=$ zYi?y%wYjh+0mdkQ{RoE#UK1ir_d&jxxyF=cpWYO5-j6~H_BYHeSpNaTR zzy<9`zAig1OMD%VND$ss@rLnnd&_~8M`KfKcU^g07n0?9!ioC@l<#ge)0_u>v%Q_X zJ}3Zg2PftZ|04(RT?tG60s1G%J{`At*N+-G}i^sjvNFt)<1*W>MEmviB%3xJxp z-q2|Kt0NqImVTtSYOi`A4(F%jxG7zP1opEPtw}w^!oQp$UL(BX#M8KYPx|&VgpEGv z*R7(D5N;nZ%I}jpb9yJ`Ez=NZs?Noa4|~RD?TbUtw**#y<{s8Cr{=R2g2WYy26xU+ z-xMdp0r2Hdn@o*pj$MWT5|U6f8O3TYpjaB&*+iFLAe-$*j^N|YxualKDh9y?&b2-OUXN-lc8j^We_bx;m za5sv2(7k+pFzx}vH6y>Cl*V*v*Ra90()a6GKp;mqyTre$ekVj9cEQ!`xz)PSfN`S7_deJ@!r#tAYpreIGcL3H#eWTLi7rvoXQI{5S)OMJv? z+j)p3!ZsT3AMOp#Lb`=7h^TWNqX~P*Sv`IB?W?1Pkb!7g8w$?OEilQ<@Ldjrn(}sM z!utpLVal4`Go6V1_?SM?1+ioTLUl=$DL{iDq?+k5{|*N!a{O848T)!)myJUMjvwfs zzwhkvTpzT9NXPo&K;~-O6&o7YAEYza^k$F`*qH1n?`dz39n>EYSi@F}_PBGZb!3#umzXQm~%H3SLWK3 zDtqn>_H+DbqfOzlM>mXBhz%^?YL!YCFks~`T+%<5M4X?2Scmy7Dk^=!?LYh9z@z=Q z6Vsa-SA46XhN7=nKHQXdkoG4~L;_OgOqMosbiEa1wRNJtz08Otmo3lT6kq953>k*T|Tmz$FPRH%t++wJYt1)S2_H z1g(hc=RJSa;_M^0`^XXEMPtcT>t#NS8$Bh%xt=LJJH4X1e803`AyfQh2QRvveTDKBQ@$i|s5Nz=SeeO8A12Lq%-`l03y zA7d3p*7Z1K@jwcVKZy3J{8ol}v?+4d)r~Jx2IBCeP~L+z+67i{f>=D*)UINt6BXDv zMU+fK5Z*efKHx>ro^+o{D2ECV%e}{u-r7NyZpWA^M^vB;&{K-Bx(H)YuSb{60v}fL za^7f8+Zk8*wEP+9AtMKv4vwX2i+V``UZU?3O@qT^#m1RwN-2FMnRe&<(+jHRL(OTg zgPMKcyglDKHq|ciX74JS6km5Q&bWW4zW_`U$pM;Q9*QgB;t zT98|OJ4732dzoE3Th-z7xezKV5Do)q8=qWF;x>1O2(IyC8OeW`YY`(PR-`pr+*wP zc_6EI`jQh1NXw4MFKG38sNA~IU)V4?#CZRt7UcPOySXy959ehGF4g>q2^sIqL1AQvd6Dq zAN;MWa9Aus@HG$qox(hQ{kQS;oCDGA))`ZtS1WZ{KB=WVfBBMYV%uTKb+t_S@nDg8 zNOdx9#Gai&nXyT&HxjxYposDXh^m4_b zSvFG@d<#!%vlJo~L5(u+&s`H{3=Bo+Z5LU(+p;Cud8@7;XZ%t|-H|@KFu$*tg_qv> zuI*tFL2y!zla+Few=sanB~Nk=tF3&uqIn#VPX}liAfh{V^iPQ0U_t^ zy+Z91M-~B16K>FHLz3K^wc(X`@?7N)apFp5HUvF#bxYSNmT9Pa>gwY1T60zCb|ZG- zUNB6g4d18(!Dp`Sk+WyucALSueV13e1~SnmwsrMChEpq_;g`~;8uWNZ{S)eALQ(H? z)KL6aKQCEI9x!+BRtzV9ocB5oSuk!DLBFe+vuV7owY4Tc-ZpaZk(9_=!trv2rQ+fC zZAhEG9y*;c@s-XTwd=w{o$gD7Be5-TqSn`{qpZ=I_ufRXgG0~h2V~|mB8!~D-hwJ( zD2yMSOQi>QX02c-8@B?X<6T7LGm)s zin)7w73=ft&6rhL+zWdpkG9zh*2hO|?B}It%<7+AoLs5sFu}M{$#3@~M+*M_@g7fB zA*H>CJ>g@})BBQfHiCZLJ@Pk3p5L)4%tF*1=cZ$#eOhA-KtBfR6ZtW43yvIB;EWPe zC1tn$3jMF|2OE$0P2a|ZYAc`%dSP~;J~_9jNP?EB_~FBcrp9nZD%kjWtDQ?{nc#C) zB@bFyhvTP3-|UBRn~EIj;)(}FX}LFSSiRJ+GzFNu9I9ywOwjIq(}BNoQq?N)b#aq< zxw$9tS)m)a;ro7lxjMT|LT=p{0P$WxUrh7%{Z^MZOYc%qkq;$24(3DW+>^2tgj{0V zo}f+>Ci1O%;fs!y$0vmyaoIlUpr{DaW?gC;rmWM3XP{@i|0&G7Zt$t3p4k(bUWGNG zgm#IgVG7wkFk~^n*9&gD)_T72?vzt>FyXhuQ_8S#;qUXAs5Bf;^h!rVGVaO#;Qdhx zbAtvAQVOduF#N*a*c6<1#Cjtwny_n%m6z4KDkcr&N{Y`(!OQMI4smet=Omm2r1=~C zZur9p$yEwHJA=L&JS9!8U?_=a1gyJEjtJJXL~ChR?e;wc^W-u%)+G zFvYI7i7g~TOkM*A?p$4SCS&|#2}R5D*Porhp}?KR1hHFeA0&!5i}B?@)`w3-e5#~} zwpDb$E`|@m5c1u8N2dEH@*;91lrB~;jdky?G|R7&(C2~?7;i)+gHR0E zHe~T-p$gyTn#)Lg;cjy|`Z7tU5EGDmkX0jIxhjJebK%~%mGJZZOU8oreG%)7#y@0j zzu{`ILLK+{gwXqn#WU^O7Em2}CK`z9kPFe55G@h_-n`JFIthn8ytYnmptIbnhC0g5 z%kVAwLb^GtpwcU)iQzB)Othif$G7d7IDSY12m43DO)b#e^NfI%{*{O*)X}equO?s_ zmA@{+TReMmpkTcfAu6xGWY3X<3(-8sqwyQkBZkg(7B#QW=4OJstH=ejPi!Cj_%uwO zfJf60Q_n1>5rdzzF1esamjowM#)NxU(+c|pxG=9!WM0@<6`mro!Bs+pFFOO)cS3=c ze#5J;&K>DpYcaIZYi;2Tc5m-ibp;NF3*}(#jkC|i!tl0Me$5>fy(bLzkf8yN(onKS zQHgmQMS<1NB|HjuX8N>f#pFX>QM{peOg%u^4TnXG)Ua*MM}lx%z<3+Qqfhq*8-_L#io*z^aDiuw zI@fXaykZu{F!%sXPX07iiITH1cI*rnCP7p$S71V+K*g(YX6cPdm=yCdsm^C*W`|yk z3NT{UKy@2Io>d#0g&jT3$f{}Or=u2lL{1&&A%gGbUrm6QbC=psKnYy)HDSG8f5Eau zK&VFKy9SzUREx=45}BUh&hW-TU!{qA3pJQf{Aq57G5uObRi-mLbiBY2ZBYSKi05Y)0DP?Xf6O z=h-d8j)wMQ*JUsq%e*z5bzRP3#xV?!jlqE+yM^h~a2?s6T5KEdlUr@EvZ#)yZcv5Q zL={sJ%Q=;T^Jm>~|9xbi&4E=6q0+FA?e0~SBwOs)A^6A$v9ZbkIT*eKIe?~tKzl%i zp{|F~mpSdS8SdbBbJix~(%3fKx4k!hbuBGzPxLm4HdMuF)gLgy=h1h!GmEL0X!X2c z-4OPv{qc(e2wO3n^=1*P)FD2&F*iBINE<2z>nL9sbPmHO zGzy?Q;XIy-+m2ly=Jr<7?Am%U>N1R;*t-)k+`aN?xhr!%O&1(33hAx&ddP@Wh^G$v8MTXf> z+%xOHD$%e#439fH{&5S5i$NW?_2vm^QDvyjbUzr!c%$~}TpSk581mffGAh+jsnePm z*e(mRsQOD5Oem_;A8tyh-<=YQJJd3wytUfcVqI}lVk<8%PT#qo1h!XtUmD|tDT7^^mC!*Vlo!%mxg+byi#>k|AmS9{9>5K zDmpDHJKmi_ork9Ui?x{hZpurO!2e-mQ6&;@&%qdGdyub4t$HR2oUFD`l6VfR-&yoL zTljiw%$fo`mfdFlx>~9o7bY~%Hrp3L4EIbY=J=#bE_)P<1ra zU3zH!z{NX6<+UE+4}!xm!`Mq)%YvHVwrwO5E;uk>90Z@`eDYW|}fsGI`0d zZq`P-ersyq<`hvsa|lJVRK}dii(}~x?i5g2l!cHwE?+_rop8%BtK;J~DU2}^O0{Cx zZ=1B<)8^|6CB<~oBIIyCYXoY7W-Uxl1GD_K5FnpuJkaG0H_jabg}Xp8VO*Q zl?$IF!pK)w>phqh=G={ABnPPj)5T^u3o@R`;*+ut3@lJ?;%EHEU4VzLVa33^w>`KF zX{d&V?bhI4|8T^{frIh0v_DloWqVR^E>Tf<^GdgqJne!@z2a}31yjbCp3TRs*HvZ= ztG()i!%)Wy)*}gk?wVLuQ(c1Ww@1?W#t|hC0;-zitHsP1E-XJ7S!OSnbNimy1G z)!BGq?r$9s9716amK0?7kUkt)WA?Is$}xRyscs zN1jvGK}E|wW65;?k<@1udBX87tB~4&aOA@%11j3*&ul>G#dn*)7*DS@dKVbtbq})> zVZOPED3o*^zGFg6=CZQS$Hxk-`__@}tHeZ8B@aFts9GI4KD`FjWu2j`nA60qp9@fr zgr*&|Rr2#Y*Sw^Sl1f=^OoQMrPMU?uVpq-$ss-PU#daTZiC|9taGL*776c6T8M&-bv^f+zHU^=a#Pa4elAs;ci5yEYFwlXKB5v} zN5uS!oBR5B;#YPZvFjaWgtdA8m1Lhv#NwSkQq3{B%4`UIz3;Xs%jUc73#}Y|th9y| ziH{shjzjc=7n~4RSJ=c^bpztBxFI&o(UChJrNfcr6$PjQYQWn|tW0zL$M>Fr!5@mO zGBi!|{tH#!@G7%m{s>I)4c{r%o#d<1%yh|bcA|jxz9OI=K!#G{0m+&Wg^*I9PD^0;K-k(2*lSKl27_5VNqI<7iVjs{8` z&PpMfr5wrLBuPdzkWgmUktDLtiZaRw$tr}5lvz<`$SBGlk?r_BUh4gRf4;vzx!3DC z9(%u-vFk3QZ7*<`jQhC-`v7}U`qLYwvPsedQWso;bXk}Beg`-4VUurk3TAHw{9G!3 zRsYS+WU0y4-kukz=aZtzytAVpa`0|D0Z@PKRxIJ{u5D|VKb9r{pOmdpE-N4heV$j_ zfbP>NqNlGaUjGz9gcnao${5gR&=}qZoED5+@YCSgxzrF)R6Z*kMi{WYlJI6+ELp8n z=+CAl8TD-P>@$(YQqx?(N6LdVXXe|j#2QpxU{9h+`P<@-^2L(?a$%Wi4Fx32<6}WM z3WH3R-723;y8Zf+xo=f6@->Bb4Ef zEZJ);?F4Ou<4ceU!jrq%;xzcC(K%E)0QUj2i z;IVL30a+XDM5%l>T1fuOH^z#*$95uIRf}JC@0p4Ib&az9&o9DU+n3~fghOlZY~%|b z960{xhiYjC&r5@Q0VEqr4C;8Afy(hXLy#lLvWYlSkw0zD0#jngPX zd)cM;z@?O86DWhFSd7{6;Sa5Jn3>!wtib@srxYRiTIMY_cB|JHXni*{6Jh+ol2JnH z9h8%{UR-deZ~M^ma1rE3uEhu*PXGK)K3q!_58>2WlOHZ*l6Z8g(^w6}91ZM4(RNI0OjL{WPssW|8eSaDa41I}$%?M+L{E-c+fE&^h zloY3f4~eG%)2H*4z2ik#{+vJt2s0*S_`_-nh+iie%s1NJD*p@QZTzU6U_|vY$}G;r!*C*!i%8 z(oVJAT;R%$+&hrLR_6@%Qr-s8F7mm;)Hp$dkDm$5+c!RX(5F;2(5Ye18`;431a_;^ z9=WIZBpR@RY?aV!aR~yl@q^hy8F3Jqg`IHm5l?Uc{yaF#KJ4ANuBDdUIj)k>y}!!`Jl2(YTnq7?&Duit#+ zE*^;*bCJ~8f+R~qM}0Cf$?3vOL%vaO_n`44Y-bQ5sEVRM02*yC^wzn zfpY?o6fLHww|@{oU;y1GNHU2%l0Lb*^YZ;VSmh0W_oGwqd~M=A7x)3jKigd?PTY|BJHAjE#nPVvrK@JWLb1P4UMA$1Nq=8>Ooy; zHGt7Gon_cAMRsTgot#{y!;lI?&R%XcFuVgR(tsBz@JaVLZ=L2YEZNwkIR7A!5p$Bp z%>|}yS=Ou`euSz8*Bb`Hfv6Bem3#a>*DC0CgEv=hz8MV8Yg~yT_!QbL=)9PUqal6D z-L|>Dd&{`3On$O)d^7uL98MOj6x?TLiyVe?)rnjPIqziGd=0q5I^;9<_WW4HqU_`~ zru`W-h{6i7vvd1Jd5Mr~+5E6?Q~9ya9A1rCvn00}mW?cfYOMr!w5Spx&_@ys`09)E-o zl&)r6H3mWl&tTw}&ASf~_^^M!3lj}F!|d!&!@KMne36TMk*b$XF-V0+!B{ij6Zt|D z=}YWfgx!XfFF>ObEK^hO@*XfZfqqARXl2{-RunkLwN1;K<>@At zYo(kF{~emTQ?*&8yYtJJrLB$_k`sfW-vrFC&9a*HZ!H94Po5xddaGQsxX(>c9wEy; zIHnt_5*HEwwK0O@l9rMz(b{gIGpV7X_v@E0_4j!Y|99rR^){Dc_tHd5F8pTlC+oOV zdoQdscbYxf3`s7XPte*;)PKQQc49HO>&d$_=_)d94g0v_NPmxkX;QS#yu*4*f+)zw{-P zfsMP^_p}=%1#X6r6b8W~O$TXjK1*afrt7`?J-&txSyDO_*5BGS?7KhXfn&;D+n&Uo zex$4Hh5FsUyzj=>g>f?=xX`a-VaAW8F=Vw9Rkspd8~dU7-u9vy-k<@3t(^rDmHasW zMlj@8l6>Zpa5TtTZpRPhLfuC&pUKnEFHh!R*5$^o6fk6ttunemZ0~tQ$hOX9*IGf5fG*E8;T21hbN_Mrri z)zQ73gSEJ6V9--vd&`-xjE?4Uf#dkL8(+oie$bG8q`Pb_wud(gS=BV?mwR`2jZ+hj z>84(betBf|WDOnJ@7?U0SHeYjz2ThYLuesZgI3!XmYfnY0ZA^pks@;B$xL})uAK1H zJo@;P9mjU^dLx)#bjv)vIZf_S`d1(=JE(Ssx4E{~C5_Y8!L;y8F z9)p~4v{*ssD$C-nr0>|7lGn8s#D*ffv&?5FGlG;k%yKqkb&titZ%0oM4W)zC$uBxr zkDqp=iOLRVPQB3L#(hfS-y|#H<(K_f?Iw`&+JtTvG^waYM#*RnYx6qm zkduwKU$|QyRIhV5_L1veF2e8iA6-&lHEw^~$4PR_j%)+ITk2HwwGn)Yb&r9^QndG{ zuP8nu;BEF7dnY$pNy&p%dP99!fJ7b`k8-*RnLplaM80s3vESOZh{y3w38L|M{H=}I ziE%KDXy*##zNqfQ)lrwt;PwxtpRg3_eTr1&j`)(kYkwEA$F;%z3Nwv@{czvy?q4S# zBD7VA$O8sw0>a&Ph6?19IvOeK=P#ZrH1T#k%0*z_E9|bmoY=7iVS-r)c#Z`05j_pM zg1|32nwHv}S~y%ebz9l*pS8VM3}Fl^9jcvuE<+2K{)b&3a_{YEcX~7A=ucKz>g?#h zVI@q?s5FNaaCi5h>zr|*tF9nq6v>r4F>?{bz_%q~p_;{C`DyAiID0OXs(YWA(0hAH z@vhCc3=_qM+UJ}jj0aTx5d0R%JAV77vQqgXMdVo^t+(MsWYY_H>^u5cbE4@-zD%SV zT4e~bhAPL3K_czcCE$V%%O~<5Gicp$R*M&_xbtHx98tOFuZEdH<7|JNNhf9A})@Q;)#S;ud!^zCdV1 z&U&L|6ST0>Ysd&BF>z*%Qs$b7Sj9%r_Vcw{4P$9=*)O39HE(mfdr#D1yU%K#8R_pu zuE$t79Jq7U9deu?3&1X5&TSjlKq_EMg{N)&Pc;<-ZH|E(jl)FTXww=sOZ2);K(O+V z+C|;~A1P!)I%EsCqQnKq2BHFK=-Ss6U&-rK5DQCeP$S~z6a@V~Mwz_za=W;)?nSUF zwgZITS|35y0#OJ-Lm4sZw|}$>u}F1{OUa;hOJp504fnm)i^viV-hVaKEClEngu8EV z{M;?V1CRUxA@kE3WQGm};nM9?IY^wp4iLca`5_>m$g5xXH;zJwL0k#z~@< zLF=BRld*WbJETM}ft#%(oP%NyhoifC{u}yzEp+^CAc67(^x*ZD8_xdP_t~{kOD&V@>#zD z?z@B9=)@RfYbt_KfO?~3+z(-S4x*kx&yKTR6g;A;JQ?-#L}5^f9>!B+%JGfW{5E`0 z*~`>nHZM$&eg`x0ICCVQp+W0NYk>8{=vz9P@Rv8I<*h{-cAv8rHs21c;9)nD1L65~Ri&g4F?Y!6*Lj-#3BdBzKGh@N{2pf}sK zWq|SWp7oHYfoz2i)HC`ef!M9T-rf;{a6=Lb*=Q|QDJe-wQ#j**NV|Z1h4P1tI{}I& zu@{^q<&oRTIE)QCvckmG8Y<(L_c$W!`oEZo5h~a8)9JQkO$lv`wyuTk3*Q!mPgcr} zLDhm4Jc#jZ&Tgrk^LnCmIBy@ep7S(L=LB5yaf=|FtSZBhZkAOy#SqMcq3OHyXRo4B z2AT}Kh${x_aPTo@;&90-))JXEwHJ1)u9S3U8Jlknr|qwPTuH!UhOB>HNgtu_G$G+h zGG>R}nGLwh&$7G>uT(^-{UBCG3$s{3C|SI$GpX)d=!6=&8tzYj4l z$@RMQkZSICACfsUzIp&h3rCl2N#u`QpvB#pN{mWeS#cU5hZm#J51H z{0NkG{#Y+n|@jb+6G z)ZC&he%CCTJ<+=VGkbde*YM40(fHeSmUcbuvQe=+TY|WdAfXW|D%0loCJzXBSq1xhPPmbZ-k}l*ML0;m*0L1Id z8|R)+LTF8Qe?oa*bI8v;%%OwT88!6s(2p4DB=al95Y5&$3;V#f@7^Xj(V4=;bUHy7tOzHc0Y&67W9gO)sOTFr=>IOKCG=FgrLx_snE;0$WNd=|Q> zWG0&D`%hIl=IncBf4JGr4XSeRrm9O;giS=vKoKF|KdhJ2*8A`8w@=2zUXPP=?byovYg2@2vfjHbsyb1=zVn%-ZS8cCMPpDqXQYL!Ptzu;LqT_oj!hg}hM0o8xzxMMsKpqxj z@ClM!i(g$rnpoRaSTmr(n>+(VRpG{DSGzm7o&Q90IW#>YecmSz${Q08f8n?hA z!x6(^=w|r+Q$e-v%CQGEEuXBufFWj#rm|p#w20kL5?f&&==rQI^7~rPNdar36#f5D zWES}hSBAe+ccgWTH(~+gwN_OyCi^z1_%E&GEkDP8du6hOnz-#XS@@_^-E&s6Ct$kx zI`q1)&G;=Zji|5K&JBD2U}L6xV>edJyHRAU&ly9|4jjDkZI`z2ty7!zJIRee%h^+FElxFu zB1J_7{({BZ{G78`_L2vTh>YY^59d=|<#qf)Z}w_ovPxk<_=oBhGs z(G?oG*V0Y*E1Wz`S0t6p6g-cwI%dk%{ixQ4JjBSiE$``QG9p?Z?)weJ4+cE@$LVM( zsQ7XA*$YGC?i#)3Iy{=We6#X~khT9qGxcy)N`~*Z{r78-Lf6kjj`7qQISWO;?7iRY6Ov2ngrE?zjRE&7er68F3PiG;eTjh3`jUtd;vcTAlPe(CeS;Tr-$;FRaAAJm@VcA$_+XN$FN_adfU z0pH>cHWTZv*y8%CVL<9%u}mOo+1q@w^I52~;gAaw&HC#A`HGl+xU6Ir+91mD&}Sod znvZE{-fLGf8Wr$XWpH_W7Ah(#=IU<(@nFQEVk!2rX{ua_IgQY+gV2EfmnqhP5rZya zNAEne>(N#zO?bG&8?8_Fo^4a}Lujs{=E;~k|B-|GQ!E+~D!BU6=SyatH}zPGNCwzH zI$6olgzT-p_SAo{P9{Hux*D^1_Xxhw;~^~5yqo&?KU2h|nlx$p3Xc6pT=-EcV*QHz zlK`kkLqBe*wgg%IUc|b?Icb@vy|adns141}0NI$|D=P^rHT3qow%RoQuh`hCw&&d@ zPM)JR6ctIYtJVpm`bT|j$s*0KRfZ+PbU%C_y;Ak(@IYZ^uZ<)ugKEYi6JyuA*GkOX z0~B>)rk@cij4UN^YuEnOp9*6I!A%`MShTH7(xKz`uat~{5rzksse3+cZYco{))TI= zJ2)XZGQKwnMhL7ykBC7$Q`@Cg07`$p91ee2S4dG-`^g4U4L>b?zat2M>sT}yM_)2|1{DdIdUClB2@~M>M1c+@35Kn8|hj?)y&OH8+^YG?w zE1;E|O|lnQlBBefr0pIwNqf1SuDiM^8~++}_bZZ%M5K4<8)mO(q;C7!Ox>Q_rL#KS zkoTGZqk15CcU^CZ4Kl|9vG)e!ooYwlI#2nxIxoCxjbddMvVH_3M+mZ|9(pUo8&?X8 zijM{bcL&w%mu0I}pPSy89af?BW=dyHg%*EUrZ@PR)HoWj$C88UpR=<`It4Z*IFu4c zQCLvKFB&N*B|W)~F4$tNEmEF;I@qiiG;rp`$ouQ=qkBORNGUyt`T1fWR{o7rCSo&* z!sW}SV>|3KTDu10#l?>(Qp5|6JPvpqXIFGFCe!T$_aOoT*cCA~0l`7=ObW=x#tDx) zUpK_Q@!9x%aWhm)GWT@pE~)F6bFCiT%}-_oR8|5<%3Ju`MJoLLM6fre) zI&5G$h-e%YK57U+r{THJTTfIyUt-OK1-BI8RCc6!1nmejHS`VOjvZmEwM463pU7UtubQ5rPw z69(R~A{n3Vgj%S~w)OwmSwrXNhrVH5d&$_DQ*|^Bf;f4dTP>*lS9*##BYq#aei6F` zva?ak+;yUj6^+-EzY}#?S~DVc)cHKG5N_H*E`U3(yUD`LKdu$;opa2dJwya_N_zwI~VSu-Z--+^Ib9PmpY=Iu02|H4HPT6xQx zG(!lZ1^O$ioFcl#36L6)ZohMXO=5F+*t)~3_Z^k8t6+C$$h^;L%}D>&%jkKtR!((A zr{(57#fvNr1cT0TZOztR)`nfr9+LZ^ za*R9klwMitbBZK<=d*3>7PlJ9E;v{+6gJ?O>Gy8O4}E)dRC#nFUz3tr8{i*bm*le6 zX#=&wD>VJGiSb8xnRk>v|MA2Zri~niFPiGd?cr@!hmR!sE&2vV6An{i9A5AQLR3L9 z!g`B3armB}5V?ji`xj2aV7742WjdtIYISE|Hrv`_|33rQ zcEyZ`;$jKcC_z;z06Qm`6Gav)&pWEYJ9c+8Kz|)XSAUNBf8&HNR^AGONoTo?ishRe4vUk~#UIdv9SBu* z;Fx4@4lX?Z(KaH(EbvPaH86F0<~cbTo*HlkGny1<`i?CQ8Ew4vU={Wk0Y5CUeV*97LwOc8QR&Bgh6(JAp>kGg1orVB6{%ggj)8{hOe zv*{st1tv5d>)(k+NRWz>dQq|YZH&r@&au|}w(n+?da1FoF!)8+mgmJ~_O%9G#j z=mq?dXGHu~w98`9EL&%j4GT-E%;A%z05?N6gg1 zvnTj6IaRWFcKFLf2WS6=8y#S5%+v@>x6KJ6I<)Y#%R&7Z|0(owQ?#e`r9TnB7ciFP zA`dnv614+FZGO{4PoMI}R-3i}=2XT%)TK%|1a`x^#hV*mc^je@NXCa62$vu_>2>=0 z`EKPoD^5otp#Q`-bbY=(V+XGaXIK6MDn+A(^g#AsZQba~#D!wR4nByzO ziy4|-Ve4sFHpKD>?itHj`;Hgy`yp?7`pAw`);R}|(<~Kz;y4 zLMEF&_vhpuV_{&2jGXJLC(^B_o?zMh?Rq9DwUOBBK&bV36om3|i585}31qbXlr|jJ zdBE(IzbN_OQHE17mz+HkUH9|W6QI7En5hXzN|jb%A^EUuH{nD`?n6pu6$>@mwre9w zG?*-J&X=;PmG|ogi`DMG9fC4HMGSu(5-vaj$_{%9*ORDB&p#wI>fX2Kmz(F?Utu>N zM{?F)#BX`TPOFG^w(6XOhYBy9Gc<0|9{jb`>*W)|Ow@j~_jZQ)TLXeI2n^(h=QmI; zI_l}xjiUE^DaN;Q*QH0h@6so&b)hrEP^{!z9K=V{Z-GuGk_>A=rXp3e*Wt%*uc-o6 zER6y6%=jYE@22x%D%NhOILol=)ILELA2b`G(QuOs%dgw=62 ztu{LzF_7*lI!`>zMW30|N)#)o3_5F6& z6;1L|U^b2J=Sxq2mnW^4K?cDegB|5Y`>h4`qwh3wc=SxV_u2h&TZ?Fs{!MKe=~!0PbCTyhVv*j>0Xm}*ND z4iAFqfljKqk=Q4f!FWbHkW9@nCr_iZXKu+#NLbbDKt4(l@-<0Bhyr&?#9ZP%(Oh;h z+0veVW%ZTH!4PzE#mY`qf(>gJ#?vi=aPNvUA`oVjZMv&AA>Cv86-AW1FaOZ{$lveX zWW8;>a$umN+La!cveaKhUxACVTQg3)t#9L$O`|1^f~C6VASVu=&b_XB_=geIvBh~8 zuga*jB#Uk_>Vqx(w$Td1*nstf2y_mD=RoE(iiwW--3lwao}L;vk&eD=*Ykq+Fo90- z42T)ILq<7_3!Hom$@^HwH?_m!lmB6FL_)8jos<8R-m;eIF?o5}FUvQ!1;_A@#sb}S#GBbV zK0YG@uG;UgGh9`OcH?fyxF^=>A5B+$B41o+0)k6t(02d(0!rfW1>FXeU>?2tl6452 z#Fe@%`~bF9JW6ZMlL7gG|Idn`vp^)Q{*?vZE3XCQv&d9=vM=>k*R8`o)D!EA`y<|= z`g@ojtRX~BR8v2|Vj`yPdn}4dAID#3+~G)%x}mZ_d^$6F#eBn}ngm^!Fc6GGTa$Ne zKMEiRJNQW85!J9|yZJiRBz_k2b_=wB-tM9g-kb#0W}if2)(uf>P9azyl!7NrK~3nC zHFoWnO%Np;oAy6Q*J$+=*&CcNyx1JgoEijz@~|~V6`>u6{=uIcuRqpi;trsb*#18O z@oQca9hU#gO&0HAq3351_gABm^`U?`aQ#<9EE;xZz;f(Qf*F6N4smL2jA)kj^RVzk zMFL4P*Iwfv#QNT1e~isMxfy2Sr!#lBUr+eN0+E|P1rbZ97QXacZ=SmzTsPQ^otk@! z&8(PE`lEr(9ZHzu(449?hUTVY)#5?uQ2=U7nhFEdron`ak}A`ujuD3YQ5)f<@wKS! zLw5bXJhv`>9`7!CdRue=1}&l{Ne1hGCdrnJW$NefOXpOZ=W=ES^NY859gZRtf}ky@ zaPZM3i$XKnSbt1KjW^d!=dLVbqArM~i}|-SSzY6&$_LdT6nCg>WpI|Hyzj*!p~It3fug5A zo#=D;W#|DHjtc##uiMVA4bD391I?asg6a|fxG3BFa8EkAL*1{7Z#cgQ>=6iqt}y;Y zp9^TW-PXp9)CN>rPakQ&Z~f(y616ke8(ik9ZJpBT)*O*eX0jZN^A*uBD{`}=m0pEL z-~fL^@>h+wI`P9Ah``(6!~Yp9WX>GNf@gBT#O@Jp3{SAXV9FWl>rs%#+hSN=Io(Yq z!U9$-Tgt=Vy}4$Ztfc3;j{hSu_{7Ylcmp@Uo{Ud@uSz4p*@)lm>PSG%9QLrM5P$qn zsO>wd*?LrSE&(R|TMu!B3COQCz+@ka5(TSc8pTyVUcD)En}Z=fha~m#?>H9|{abe0 zR@a#7sJAtWu>S7e5z-~cc!m`(JxJEVEH@w#?=o%U#8fkwxpbb#tGuqJ(g&)|b=;%4 zq}ToHQ7{evYP&BOarKkkVHN=N()WGCOJiX|)wd?>GwBD~Q2yg<_DlZH_0JTvL5azIExX(-^74X+yV^JQak$7>dcjw{r zRF6&T6+7AxicZ6n1UFAt^DPviwSpq@ypaGBOIMZg-JoNUF6$u?;qABYaARlgqCvhp z(Uq;;As$8tzTZUsl^f-s#G*4e)A#2*deVGB@*W8>Ga3HZ#!%&Ht`k!WAIRUF)aU3V zJV1{MJlpAOtzZfHe|s8CgHoe3A`oo!S{~ws9lcW8{0vo$B11?2Q$_)b+v`J_i4Q>V z30oI}_1RxPd7xk{!43@->%9%8N$f$Zr$L{)Rf2seQZx9gIYNo4Gsr|>D}+VBqYTFm z$ZT)Vk1-C{n< zpqd!SjHq3ScMM_e%P8BJg$gmG|Eq{5TR{GKJ^E*IjZW11({9rL@C$peY z(A+!Tdsl=J0o2jkIHIYaZe20#-gn5*$6&yi!b~(lAMg3x_qbT9_-3ODR41=a9!ncb(E|IB|z?EC^o{-1LEiXR<<2~eBERUiR8T1?Iwzc|A zYU3{=_>R z!h^5k2Wi!JujXPbMQIP`E_gW@UQX;wtAEVnLQq!O6moos09B!1^gnZGX{^G!z92hw z*xk%>UU|ptQ!Jen+VpA@X$7}D0gmR&ON+|J8-1(YWz%`NQpOO%LK^RaP|aP`{A*MS zS+|YEM=}#3+4+|mN8Z^)BusPa3`bly+s(77uKrj?4dQ_5UlFNs>>lR_>r~akuR;-o z!$FYVSoi2^G*>|a|`%DRn%SH)k)9}!Rl zGjpMLwXMiDDU`ji(0%ppu^Yc1uy-D&Hr(a0#{e?(pq$2R_4`u^ZzmpK66cJS*L5XE;lBE#J(y4eO5bCv6gYS>VBVwod-UC zHhR=r^YutwTR3~)an9ks)m1wI9W3_5;-jOBYs<3Jkvn=hTe-03j{4yTMAsTRE25{% zf6p?~P&TMf`!~n0?~@gQ zIh^u1hqj7elhZ#oyyiqG#Eto|ig@DkPuF#^&W-6{!L$aU+-t+DxnB(g$TDnRzSalR zQloA>A1`gqI-;o%F2Re#q<=r(?^6S?iLjcxdmrsze#8+5d34PhF%r@mzng z%vMlc<^>`h$9p+Nc$W>H&p!#$SbD+imn|ZX%e!B()L$7t-SUyecJk zec9D}=1ThO?X2b%EZLOxH&PHFraPE+KP~Fnh0X-s5MH;$?eZND30NcNqzw@>Ey`Mr z+0Nz2T(6_51et7bMjqw$!S)cWZ8ls$R{M>MVX~gp`Ha?}B4{of*LYgU_#-Y-sOoK91d11AiaoycjybhCBWn%W}AsoMR z_!Os`a-v&*%66K_Qn`mO?K~{m>t1y936EY8z*@u-umdCPLx@p|3@GdeIiNu!*NUdX ze(+(x-Nh*&>EE=rRZSiE=!hK34%M^i`<((FdKGcHnF3#TX|wB~}>hH=&7qmm1HeP-0cgeuUtC`Fe+pjN|*|JPHb*B7RjpL?p>>Z zpSsK%a5r+z5zB?>Oa2s2;Hd1r9)|+=N9XNa&Z+jgpRAS`r;$c>fZ>X(+&kuA~){=b?O)9VrVi z%f6B#;eJW(t1IZeI4Qg~R(8%4%14!ig$y&{V2S=;&8 zqOYZr{si^_3)a4eL-Z)kC=72?z77c7yn=_ryhQH%xXO8zL||W`tr|LlylGLkz^;Zn zuJdERePVb!bCn{(CzG}hS+qOuXT^7QlShh`%ucRHBsKBad*2SGO<%ZC)=K@QN6{W# z9|-NdhxdOAUyXdLx-Jd;dApk`pH1tZR=kLOf%Cri1}bNHP@&goq*l`H+{@NJkkjnM zg;RdB{Q{ZxGxd#e&ouI7sN-tW?w#bxvGLiXVIajCDn3bSb{F-s*ZuN*J|7h1&}^%O ztJY-;_$J5Or9($P`EN#Iz5OAy z`o8l-1Vi4kNf7hI{iSk7{&;`Oqdn|%X~eLMt$BQ)^{TiqJ zxKm?`;r*L(h`D@PRDRTrO?G(`VPdIPr8>I0eup@%GJBvMiTQdRd^6Fz>DDdIy_Z|X zwdqLGEw-BcIDR`9^Ra3E{IH!yl1*USZrS9zl-1jok9Ya+>RhA-d-YcyZMb4UYs4)z zIKhZK=*eCkRHC@T0>TP-zLe?Jr;(nr$L)R~`h`7Ii}}OIhfKSk{HWQIoNRHO#Jkd;NhdKB4Cht9D zr0Nl%|9bq-*20crdXlu@G5H5CEm+r`DLz@uRdfX_dy&1#tG5N2{6yW~ioTX=nC5jS zNsujnql|)>H^qKTR~5V@)Nr$~Bluu7F-M&X_ujlnX;EF3LwCnO;3!Nv-x95-r&iM8 z+-u!#uGg8)oX2q4)~SE5t&?v`;Q*-^y z`;}c-d}x=?YE27J1taA(Z^MSpjsXsrNFVf+0P;z_#;0XD9x6z6!DqT^HQ+FuG@JOlNe)t6wf*XqfUkdtaW5OJ^Sqa`V9f&(e1B zk1uHOhGX3wbk$k=1HOGDCTSKt-j_G)#N{;=tEzG84Yx~w9aeC7|0ZJ1EP zt_<7X^=(fDS77faam~`49+(Fzh`|YYB#dJ%5s4IB%qavtjbo0 z5#M+||Hvj!*q@MHZozsoW~g8J%I-tKf8f6S4+|G|h-Bi%6>&75_t2;-EMX9On;w_r z4Qa;XTiVSJ(8-&JojY5fMr6#`suA>y1u-IRiqb*plbNfx3oBTnkn6yOazuA^3X1_P zpT#0#iK6SVb^J@aG89lCboY|;78d=r@L&2x8TPAs3pEI~lP#al0Tf>LP+=~lbvwDs z>oGR3vSQk7WOW_DdC$4j;ZYF}P*ustj=t-XnsccL_IjC|lV4RiXt}GDe)ZWvo2rN| zsxK;!WTN$I^%~pZLsuDFCvJubV7{L3Uo?Yr*{Po7xWMJG+oennO91Hrx;!M^I+2-N zbzy2x0>Q!O^0<@SL#tB~<`)0RhQC79!BqkOpjDJ%T631uQgQ1~=cB667!c9n_g|ZS z#_k`KPAr_?va?p2;X~&1s@#K+dQUMWs{!w?kEjRH^AtiQgxa!Gr!ylRX|FF z^#G~ucqKF_g5;6))Z)`i60J@xVHUM1*;^*M0J*IllRjJBn!}(V4BHz>C{V`D!;`@=BjVNGIuJbSBaGz!*}v3{w38F zy;kw&?WBS5DIvJ+%SSBL`&y`g;{VLo9n$YS)aeGU&U|fO8U%m(8iOKNM6wnZ< zReZGcI>?5nSb_s~@qrdz+VsVof;Z1j?MsCd>!M75?(#ye4lX5z9zcKan6>9yU+UL7^nR&lTzaB>4nIC=Ano19z8oM5aG2~NL}4n#3k zg~7VZ*vc-#h12+ySXf$(Pgew;a40#7qSsjH$ez?j#qgiP(d^=5u;4Db;Dh8=NVg(|de(z2>|_5cR6YYXvsJ^>sGQzY^5P!0dX(AC08vPOq}8R`?uicu4W&D{64~ z{f9&)G$V7yB_F1{t06SIN~N6rs%{i$2JjTDo{~p-G~U_gkL;{^H>J%>{?WEbtsGme z(4K7=Vff}S;o|d0?@fj6e=)^~!D;+D*-l6E%z`x_gjg9d#MC};QU;Y6o5Em|rK_BP zEo(khhx^0~QM2A)Hq}1z;b|1u;1!8Oj<)s|EB=JMCjl02;tiGRvyWN52HX8Pk-TKo znm5S;0l33xrvl!{@x^@UhHQge=qZCPi?uDtUeWROS3s1D+YYgC;j$w(rH0wN60Y&F zXkvmGXe$wI%}BjQL&~tTPZg!F4%U!+p&QrC=WcetF`G64-9t}JO|Y{x=lJt|dG8hr zn5$NR4~4w?lQd`3fjos=&UQ_8p9e*+*_vwC6T7MEX@nfK$FUgZ^Op?6QNr7V{9l;a z;QbJ}ck)?67RQfK5%cxfRIpY*INjI6WxA$RRq1Fld!#X-nBX8eB^5oTN&N5dX>Vq2ECc*E!j z{+xedF)9w4wHE6D-rypg!FK#UN5Eo2gCpWfq!)oC4Aj`v1Rz64&j?h2&9n}#AJw=wS;%Ccpj;HLa z66V5*3VJj#EiY#ef4Tn=6-D$-uy+W6+Sr5s^%xr$7L$HWJGNeg-`(oY#@U3wAo}Y} zkg0HpG%<#^N)&D4GRg3YlhR-2`$LqyoK?N(6B7FqmK|Jn8eOQ>QT)H!2AMjIqsfjs z_q1Mw&t0~aBMClBh0ueB`vYZZ0WMr#%fchU*>z&rkD;>Drm8K=R$v6NJoq0Yh~{hO zQBY>tkzKftT@zTIY1P@Qlx?BkV3tu6%)tv9exHsai)_+sVTY#AY@wTot$^ zGrmN*$RYm7HAPA>ue+s7HS=%~lGg&3rPhtij$85)

f!#)$c?=vw*2qZ6}lh>Ugc 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 literal 0 HcmV?d00001 diff --git a/viscoll-app/src/assets/logo_white.svg b/viscoll-app/src/assets/logo_white.svg new file mode 100644 index 00000000..0a0a4ee6 --- /dev/null +++ b/viscoll-app/src/assets/logo_white.svg @@ -0,0 +1 @@ +Asset 4 \ 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 0000000000000000000000000000000000000000..13d6923b8c5ac6317cd13c111c1b78d428a34636 GIT binary patch literal 27106 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 this.Notes[noteID].show}); + for (let noteIndex = 0; noteIndex < notesToShow.length; noteIndex++) { + // Draw note text + let x = 0; + let fontSize = this.spacing*0.40; + let numChars = this.path.bounds.width/fontSize*2.4; + if (this.attachment.bounds.width>0) { + // This leaf has an attachment (glue, etc) + // Place text to the right of attachment drawing + x = this.attachment.bounds.right+5; + } else if (this.isConjoined()) { + // This leaf is conjoined + x = this.path.segments[1].point.x; + } else { + // Separate leaf + x = this.path.segments[0].point.x + 10; + } + let textNote = new paper.PointText({ + content: this.Notes[notesToShow[noteIndex]].title.substr(0,numChars), + point: [x, this.y - noteIndex*(this.spacing*0.7) - 10], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + this.textNotes.addChild(textNote); + } + 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==="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_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 + 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()) { + if (this.isConjoined()) { + this.indent = (this.prevPaperLeaf().indent+1); + } else { + // The previous leaf is conjoined, so add 1 indent + 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==="Flyleaf"||this.leaf.type==="Endleaf") { + this.path.strokeColor = "#727272"; + } else { + this.path.strokeColor = this.strokeColor; + } + }, + setVisibility: function(visibleAttributes) { + this.visibleAttributes = visibleAttributes; + this.showAttributes(); + }, + showAttributes: function() { + if (this.visibleAttributes.side.folio_number && this.visibleAttributes.side.texture) { + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit attribute text + this.path.segments[this.path.segments.length-1].point.x = this.width-this.spacing*2; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.width-this.spacing*1.8; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.width-this.spacing*1.8; + } + this.textLeafOrder.set({point: [this.width-this.spacing*1.8, this.textLeafOrder.point.y]}); + this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: this.recto.folio_number + " " + this.recto.texture}); + this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: this.verso.folio_number + " " + this.verso.texture}); + } else if (this.visibleAttributes.side.folio_number) { + 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 = this.width - this.spacing; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.width - this.spacing*0.8; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.width - this.spacing*0.8; + } + // Adjust text + this.textLeafOrder.set({point: [this.width-this.spacing*0.80, this.textLeafOrder.point.y]}); + this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: this.recto.folio_number}); + this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: this.verso.folio_number}); + } else if (this.visibleAttributes.side.texture) { + if (this.leaf.stub === "None") { + // Reduce leaf width so we can fit texture text + this.path.segments[this.path.segments.length-1].point.x = this.width - this.spacing; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.width - this.spacing*0.8; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.width - this.spacing*0.8; + } + // Adjust text + this.textLeafOrder.set({point: [this.width-this.spacing*0.80, this.textLeafOrder.point.y]}); + this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: this.recto.texture}); + this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: this.verso.texture}); + } else { + // Reset leaf + if (this.leaf.stub === "None") { + this.path.segments[this.path.segments.length-1].point.x = this.width; + this.highlight.segments[this.highlight.segments.length-1].point.x = this.width + 5; + this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.width + 5; + } + // Reset texts back + this.textLeafOrder.set({point: [this.width+10, this.textLeafOrder.point.y]}); + this.textRecto.set({point:[this.width+18, this.textRecto.point.y], content:""}); + this.textVerso.set({point:[this.width+18, this.textVerso.point.y],content:""}); + } + }, +} +// Constructor for leaf +function PaperLeaf(args) { + this.manager = args.manager; + this.Notes = args.Notes; + this.leaf = args.leaf; + this.recto = args.recto; + this.verso = args.verso; + this.order = this.leaf.order; + this.parentOrder = args.Groups[this.leaf.parentID].order; + this.conjoined_to = this.leaf.conjoined_leaf_order; + this.indent = null; + this.origin = args.origin; + this.viewingMode = args.viewingMode; + this.width = this.viewingMode? args.width*0.88 : args.width*0.92; + 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.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==="Flyleaf"||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.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..fc6ae123 --- /dev/null +++ b/viscoll-app/src/assets/visualMode/PaperManager.js @@ -0,0 +1,604 @@ +import paper from 'paper'; +import PaperLeaf from "./PaperLeaf.js"; +import PaperGroup from "./PaperGroup.js"; + +PaperManager.prototype = { + constructor: PaperManager, + createGroup: function(group) { + let g = new PaperGroup({ + manager: this, + group: group, + y: this.groupYs[group.order-1], + x: (group.nestLevel-1)*(this.spacing), + 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, + }); + + 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.order)) { + 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[leaf.order]), + 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[leaf.order-1], + 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[leaf.order], + strokeColorFilter: this.strokeColorFilter, + visibleAttributes: this.visibleAttributes, + viewingMode: this.viewingMode, + }); + 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.order)) { + 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; + for (let groupID of this.groupIDs) { + const group = this.Groups[groupID]; + if (group.nestLevel === 1) { + this.groupYs.push(currentY); + currentY = this.calculateYs(group.memberIDs, currentY, this.spacing); + currentY = currentY + this.spacing; + if (group.memberIDs.length===0) { + currentY = currentY + this.spacing/2.0; + } + } + } + + // 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 tacketing + this.drawTackets(); + + 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) { + // Remove existing tacket + this.groupTacket.removeChildren(); + this.groupTacketGuide.removeChildren(); + this.groupTacketGuideLine.removeChildren(); + + this.tacketToolIsActive = true; + 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); + + 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.toggleTacket(""); + if (targets.length>0) { + let targetLeaf = targets[targets.length/2]; + this.addTacket(targetLeaf.leaf.parentID, targetLeaf.leaf.id); + } else { + // Redraw old tacket + this.drawTackets(); + } + } + 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.Leafs[memberID].order); + 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(); + } + } + + }); + } + }, + drawTacketGuide: function(groupID) { + const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); + const guideY = targetGroup.path.bounds.height/2; + const guideX = targetGroup.path.bounds.left; + 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+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-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-10, guideLine.segments[1].point.y+10); + let guideLineX1 = new paper.Path(); + guideLineX1.strokeColor = "#ffffff"; + guideLineX1.strokeWidth = 3; + guideLineX1.add(new paper.Point(guideX-10, guideLine.segments[0].point.y-10)); + guideLineX1.add(new paper.Point(guideX+10, guideLine.segments[0].point.y+10)); + let guideLineX2 = new paper.Path(); + guideLineX2.strokeColor = "#ffffff"; + guideLineX2.strokeWidth = 3; + guideLineX2.add(new paper.Point(guideX-10, guideLine.segments[0].point.y+10)); + guideLineX2.add(new paper.Point(guideX+10, guideLine.segments[0].point.y-10)); + + let guideText = new paper.PointText({ + content: "DRAW TACKET LINE", + point: [guideX+20, targetGroup.path.bounds.y + guideY - 20], + fillColor: "#000000", + fontSize: 12, + }); + let guideTextRectangle = new paper.Rectangle( + new paper.Point(guideX+15, targetGroup.path.bounds.y + guideY - 35), + new paper.Size(guideText.bounds.width + 10, 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()); + }, + drawTackets: function() { + this.paperGroups.forEach((group)=> { + if (group.group.tacketed!==null && group.group.tacketed.length>0) { + const targetLeafMemberID = group.group.memberIDs.find((memberID)=>{return (memberID.charAt(0)==="L"&& memberID===group.group.tacketed)}); + if (targetLeafMemberID!==undefined) { + const paperLeaf = this.getLeaf(this.Leafs[targetLeafMemberID].order); + let tacketPath1 = new paper.Path(); + tacketPath1.name = "tacket1"; + tacketPath1.strokeColor = this.strokeColorTacket; + tacketPath1.strokeWidth = 3; + tacketPath1.add(new paper.Point(15, paperLeaf.path.segments[0].point.y-2)); + tacketPath1.add(new paper.Point(paperLeaf.path.segments[0].point.x+this.strokeWidth, paperLeaf.path.segments[0].point.y-2)); + tacketPath1.add(new paper.Point(tacketPath1.segments[1].point.x+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(15, paperLeaf.path.segments[0].point.y+2)); + tacketPath2.add(new paper.Point(paperLeaf.path.segments[0].point.x+this.strokeWidth, paperLeaf.path.segments[0].point.y+2)); + tacketPath2.add(new paper.Point(tacketPath2.segments[1].point.x+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[group.order-1]; + 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[firstMember.order-1]; + return firstLeafY; + } + }, + getYOfLastMember: function(groupID) { + const group = this.Groups[groupID]; + const lastMember = this.getLastMember(groupID); + if (lastMember && lastMember.memberType==="Group") { + let y = this.groupYs[lastMember.order-1]; + return y+((lastMember.nestLevel-group.nestLevel)*this.spacing + (this.spacing)); + } else if (lastMember && lastMember.memberType==="Leaf") { + let lastLeafY = this.leafYs[lastMember.order-1] + this.strokeWidth + this.spacing/2.0; + return lastLeafY+((lastMember.nestLevel-group.nestLevel-1)*this.spacing); + } else { + return 0; + } + }, + getLastMember: function(groupID) { + let lastMember = null; + for (let memberID of this.Groups[groupID].memberIDs) { + let memberObject = this[memberID.split("_")[0]+"s"][memberID]; + if (lastMember===null || (memberObject.memberOrder>lastMember.memberOrder)) { + lastMember = memberObject; + } + if (memberID.charAt(0)==="G" && memberObject.memberIDs.length>0) { + let result = this.getLastMember(memberID); + if (result) lastMember = result; + } + } + return lastMember; + }, + getGroupHeight: function(group) { + if (group.memberIDs.length>0) { + let height = this.getYOfLastMember(group.id) - this.groupYs[group.order-1]; + 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 notesToShow = memberObject.notes.filter((noteID)=>{return this.Notes[noteID].show}); + + if (memberObject.memberType==="Leaf" && memberObject.memberOrder===1 && notesToShow.length>0) { + // First leaf in the group with a note + this.multipliers[memberObject.order] = multiplier; + currentY = currentY + spacing*(notesToShow.length+1); + if (i > 0 && members[i-1].memberType==="Group" && members[i-1].memberIDs.length) { + // Previous sibling is a group with children + currentY = currentY - memberObject.nestLevel*spacing; + } + this.leafYs.push(currentY); + if (i===(members.length-1)) { + // Last member of group + currentY = currentY + (memberObject.nestLevel)*spacing; + } + } else if (memberObject.memberType==="Leaf" && memberObject.order > 0) { + this.multipliers[memberObject.order] = multiplier; + currentY = currentY + spacing*(Math.max(1,notesToShow.length)); + if (i > 0 && members[i-1].memberType==="Group" && this.Groups[members[i-1]].memberIDs.length) { + // Previous sibling is a group with children + currentY = currentY - memberObject.nestLevel*spacing; + } + this.leafYs.push(currentY); + if (i===members.length-1) { + // Last member of group + currentY = currentY + (memberObject.nestLevel)*spacing/4; + } + } else if (memberObject.memberType==="Group") { + currentY = currentY + spacing; + if (i > 0 && members[i-1].memberType==="Group" && this.Groups[members[i-1]].memberIDs.length>0) { + currentY = currentY - memberObject.nestLevel*spacing; + } + this.groupYs.push(currentY); + if (memberObject.memberIDs.length<1) { + // No nested members, so give padding equal to + // the height of this empty group, which is spacing + currentY = currentY + spacing; + if (i===members.length-1) { + // If we are the last member and it's empty group + currentY = currentY + (memberObject.nestLevel)*spacing; + } + } + // 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)); + }, +} +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.toggleTacket = args.toggleTacket; + this.addTacket = args.addTacket; + this.tacketToolIsActive = false; + this.tacketToolOriginalPosition = 0; + this.slideForward = true; + 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/axiosConfig.js b/viscoll-app/src/axiosConfig.js new file mode 100644 index 00000000..996cd11c --- /dev/null +++ b/viscoll-app/src/axiosConfig.js @@ -0,0 +1,49 @@ +import axios from "axios"; + +let API_URL = '/api'; + +// IN DEVELOPMENT +if (process.env.NODE_ENV === 'development') { + API_URL = 'http://localhost:3001'; +} +export const client = axios.create({ + baseURL: API_URL, + responseType: 'json' +}); + +export const clientOptions = { + interceptors: { + request: [ + ({getState, dispatch, getSourceAction}, request) => { + if (getState().user.token) { + request.headers['Authorization'] = getState().user.token + } + return request; + } + ], + response: [{ + success: function ({getState, dispatch, getSourceAction}, response) { + dispatch({ type: "HIDE_LOADING" }); + if (response.config.successMessage){ + dispatch({ + type: "SHOW_NOTIFICATION", + payload: response.config.successMessage + }); + setTimeout(()=>dispatch({type: "HIDE_NOTIFICATION"}), 4000); + } + return Promise.resolve(response.data); + }, + error: function ({getState, dispatch, getSourceAction}, error) { + dispatch({ type: "HIDE_LOADING" }); + if (error.config.errorMessage) { + dispatch({ + type: "SHOW_NOTIFICATION", + payload: error.config.errorMessage + }); + setTimeout(()=>dispatch({type: "HIDE_NOTIFICATION"}), 4000); + } + return Promise.reject(error); + } + }] + } +} diff --git a/viscoll-app/src/components/authentication/Login.js b/viscoll-app/src/components/authentication/Login.js new file mode 100644 index 00000000..b05bbad0 --- /dev/null +++ b/viscoll-app/src/components/authentication/Login.js @@ -0,0 +1,109 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ResendConfirmation from './ResendConfirmation'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import { btnLg } 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 + * @param {string} v new value + * @param {string} type text field name + * @public + */ + onInputChange(v, type) { + this.setState({[type]: v}); + } + /** + * Submit login information and clear any message + * @public + */ + submit = (e) => { + e.preventDefault(); + this.setState({ error: ""}); + this.props.action.loginUser({email: this.state.email, password: this.state.password}); + } + + /** + * Cancel button. Resets local Login state. + * @public + */ + 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} /> + this.onInputChange(v,"password")} name="password" type="password" floatingLabelText="Password" {...floatFieldDark} /> +

+ {submitButton} +
+ +
+

+ Forgot password? + ; + if (this.state.error && this.state.error.includes("unconfirmed")) { + content = + } + return ( + content + ); + } + static propTypes = { + /** History object provided by react router. */ + history: PropTypes.object, + /** User object from the store. */ + user: PropTypes.object, + /** Dictionary of actions. */ + action: PropTypes.objectOf(PropTypes.func), + /** Cancel callback to close this component. */ + tapCancel: PropTypes.func, + /** Callback to show the reset password form. */ + toggleResetRequest: PropTypes.func, + } + +} + +export default Login; diff --git a/viscoll-app/src/components/authentication/Login.md b/viscoll-app/src/components/authentication/Login.md new file mode 100644 index 00000000..7f5e15c2 --- /dev/null +++ b/viscoll-app/src/components/authentication/Login.md @@ -0,0 +1,7 @@ + +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| email | string | Stores current value of the email input field | +| password | string | Stores current value of the password input field | diff --git a/viscoll-app/src/components/authentication/Register.js b/viscoll-app/src/components/authentication/Register.js new file mode 100644 index 00000000..02d40dde --- /dev/null +++ b/viscoll-app/src/components/authentication/Register.js @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import {btnLg} 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 + * @param {string} v new value + * @param {string} type text field name + * @public + */ + onInputChange = (v, type) => { + this.setState({[type]: v}); + } + /** + * Submit registration information + * @public + */ + submit = (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 cancelMessage = "Cancel"; + if (this.props.user && this.props.user.registerSuccess) { + cancelMessage = "Okay"; + } + let cancel = ( +
+ +
+ ); + let registerForm = ( +
+ this.onInputChange(v, "name")} + name="name" + floatingLabelText="Name" + {...floatFieldDark} + /> + + this.onInputChange(v, "email")} + name="email" + floatingLabelText="E-mail" + {...floatFieldDark} + errorText={emailError} + /> + + this.onInputChange(v, "password")} + name="password" + floatingLabelText="Password" + {...floatFieldDark} + errorText={passwordError} + type="password" + /> + +

+ + {cancel} + + ); + + const successMessage = ( +

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

+ ); + + if (registerSuccess) { + return successMessage; + } else { + return registerForm; + } + } + static propTypes = { + /** User object from the store. */ + userState: PropTypes.object, + /** Dictionary of actions. */ + action: PropTypes.objectOf(PropTypes.func), + /** Cancel callback to close this component. */ + tapCancel: PropTypes.func, + } +} + +export default Register; diff --git a/viscoll-app/src/components/authentication/Register.md b/viscoll-app/src/components/authentication/Register.md new file mode 100644 index 00000000..d4ff5fcb --- /dev/null +++ b/viscoll-app/src/components/authentication/Register.md @@ -0,0 +1,8 @@ + +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| name | string | Stores current value of the name input field | +| email | string | Stores current value of the email input field | +| password | string | Stores current value of the password input field | diff --git a/viscoll-app/src/components/authentication/ResendConfirmation.js b/viscoll-app/src/components/authentication/ResendConfirmation.js new file mode 100644 index 00000000..a926d55e --- /dev/null +++ b/viscoll-app/src/components/authentication/ResendConfirmation.js @@ -0,0 +1,63 @@ +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'; + +/** + * 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 + * @param {string} v new value + * @param {string} type text field name + * @public + */ + onChange(v, type) { + this.setState({[type]: v}); + } + + /** + * Send confirmation email + * @public + */ + 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} /> +

+ + ; + + return ( +
+

{this.state.message}

+ {form} +
+ +
+
); + } +} +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..71259f64 --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPassword.js @@ -0,0 +1,84 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +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 + * @param {string} v new value + * @param {string} type text field name + * @public + */ + onInputChange = (v, type) => { + this.setState({ [type]: v }); + } + + /** + * Validate password input and submit password change + * @public + */ + submit = (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} /> +

+ + + ); + } + + static propTypes = { + /** Callback function to submit password change. */ + resetPassword: PropTypes.func, + /** Reset password token. */ + reset_password_token: PropTypes.string, + /** Success callback to close this component. */ + handleResetPasswordSuccess: PropTypes.func, + } +} + +export default ResetPassword; diff --git a/viscoll-app/src/components/authentication/ResetPassword.md b/viscoll-app/src/components/authentication/ResetPassword.md new file mode 100644 index 00000000..78797bcb --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPassword.md @@ -0,0 +1,9 @@ + +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| password | string | Stores current value of the password input field | +| passwordConfirm | string | Stores current value of the password confirm input field | +| resetMessage | string | Stores current message to display above the form | + diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.js b/viscoll-app/src/components/authentication/ResetPasswordRequest.js new file mode 100644 index 00000000..77913218 --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPasswordRequest.js @@ -0,0 +1,89 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import { btnLg, btnMd } 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 + * @param {string} v new value + * @param {string} type text field name + * @public + */ + onInputChange(v, type) { + this.setState({[type]: v}); + } + + /** + * Validate email address and submit password reset request + * @public + */ + resetPasswordRequest = (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 cancelMessage = "Cancel"; + let submit = +
+ +
; + if (this.state.requested) { + cancelMessage = "Okay"; + submit = ""; + } + return ( +
+

{this.state.resetMessage}

+ this.onInputChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} /> + { submit } +
+ +
+ + ) + } + static propTypes = { + /** Dictionary of actions. */ + action: PropTypes.objectOf(PropTypes.func), + /** Cancel callback to close this component. */ + tapCancel: PropTypes.func, + } +} +export default ResetPasswordRequest; diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.md b/viscoll-app/src/components/authentication/ResetPasswordRequest.md new file mode 100644 index 00000000..ea0c70d1 --- /dev/null +++ b/viscoll-app/src/components/authentication/ResetPasswordRequest.md @@ -0,0 +1,7 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| email | string | Stores current value of the email input field | +| resetMessage | string | Stores current message to display above the form | +| requested | boolean | Value is `true` if the user pressed the submit button. This state is used to know when to show a confirmation message upon user submit. | diff --git a/viscoll-app/src/components/collationManager/TabularMode.js b/viscoll-app/src/components/collationManager/TabularMode.js new file mode 100644 index 00000000..5f731069 --- /dev/null +++ b/viscoll-app/src/components/collationManager/TabularMode.js @@ -0,0 +1,42 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Group from './tabularMode/Group'; + + +/** Stateless functional component that mounts the root groups. */ +const TabularMode = (props) => { + const { filters } = props.collationManager + const { Groups, groupIDs } = props.project + let group_components = []; + for (let groupID of groupIDs){ + const group = Groups[groupID] + if (group.nestLevel === 1) + group_components.push( + + ); + } + + let emptyResults = false; + const activeFiltersLength = filters.Groups.length + filters.Leafs.length + filters.Sides.length + filters.Notes.length; + if (activeFiltersLength===0) + emptyResults = true && filters.hideOthers && filters.active + + return ( +
+ {emptyResults ?

No objects match the query

: group_components} +
+ ); +} + +TabularMode.propTypes = { + /** Callback for handling clicking on an object (group or leaf) */ + handleObjectClick: PropTypes.func, +} + +export default TabularMode; diff --git a/viscoll-app/src/components/collationManager/ViewingMode.js b/viscoll-app/src/components/collationManager/ViewingMode.js new file mode 100644 index 00000000..bb550446 --- /dev/null +++ b/viscoll-app/src/components/collationManager/ViewingMode.js @@ -0,0 +1,134 @@ +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.06, + strokeWidth: 0.016, + 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.collationManager.visibleAttributes, + 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.collationManager.visibleAttributes !== nextProps.collationManager.visibleAttributes || + this.props.project.Notes!==nextProps.project.Notes + ); + } + + componentWillUpdate(nextProps) { + 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.collationManager.visibleAttributes); + this.drawOnCanvas(); + } + } + + componentWillUnmount() { + window.removeEventListener("resize", this.drawOnCanvas); + } + + /** + * Draw canvas + * @public + */ + drawOnCanvas = () => { + // Create leaves through manager + this.updateCanvasSize(); + this.state.paperManager.draw(); + } + + /** + * Update canvas size based on current window size + * @public + */ + updateCanvasSize = () => { + // Resize the canvas + let maxWidth = window.innerWidth-window.innerWidth*0.75; + document.getElementById("myCanvas").width=maxWidth; + this.state.paperManager.setWidth(maxWidth); + } + + + render() { + let canvasAttr = { + 'data-paper-hidpi': 'off', + 'height': "99999999px", + 'width': window.innerWidth-window.innerWidth*0.75, + }; + + + let leafID, rectoURL, versoURL; + if (this.props.selectedObjects.type==="Leaf"){ + leafID = this.props.selectedObjects.members[0]; + const leaf = this.props.project.Leafs[leafID]; + const recto = this.props.project.Rectos[leaf.rectoID]; + const verso = this.props.project.Versos[leaf.versoID]; + rectoURL = recto.image.url; + versoURL = verso.image.url; + } else if (this.props.selectedObjects.type==="Recto") { + const recto = this.props.project.Rectos[this.props.selectedObjects.members[0]]; + rectoURL = recto.image.url; + } else if (this.props.selectedObjects.type==="Verso") { + const verso = this.props.project.Versos[this.props.selectedObjects.members[0]]; + versoURL = verso.image.url; + } + + return ( +
+
+ +
+ +
+ ); + } +} diff --git a/viscoll-app/src/components/collationManager/VisualMode.js b/viscoll-app/src/components/collationManager/VisualMode.js new file mode 100644 index 00000000..b199c5cc --- /dev/null +++ b/viscoll-app/src/components/collationManager/VisualMode.js @@ -0,0 +1,147 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +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.toggleTacket(""); + 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.collationManager.visibleAttributes, + toggleTacket: this.props.toggleTacket, + addTacket: this.addTacket, + }) + }, ()=>{this.drawOnCanvas();}); + } + componentWillUnmount() { + this.props.toggleTacket(""); + this.state.paperManager.deactivateTacketTool(); + window.removeEventListener("resize", this.drawOnCanvas); + } + + shouldComponentUpdate(nextProps, nextState) { + 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.collationManager.visibleAttributes !== nextProps.collationManager.visibleAttributes || + this.props.tacketing !== nextProps.tacketing + ); + } + + 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.collationManager.visibleAttributes); + this.drawOnCanvas(); + if (nextProps.tacketing!=="") { + this.state.paperManager.activateTacketTool(nextProps.tacketing); + } else { + this.state.paperManager.deactivateTacketTool(); + } + } + } + + + addTacket = (groupID, leafID) => { + let updatedGroup = { + tacketed: leafID, + } + this.props.updateGroup(groupID, updatedGroup); + } + + /** + * Update canvas size based on current window size + * @public + */ + updateCanvasSize = () => { + // Resize the canvas + let maxWidth = window.innerWidth-window.innerWidth*0.46; + document.getElementById("myCanvas").width=maxWidth; + this.state.paperManager.setWidth(maxWidth); + } + + /** + * Draw canvas + * @public + */ + 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 ( +
+ +
+ ); + } +} +VisualMode.propTypes = { + /** Array of root group objects */ + groups: PropTypes.arrayOf(PropTypes.object), + /** Callback for handling clicking on an object (group or leaf) */ + handleObjectClick: PropTypes.func, + /** Dictionary of selected objects */ + selectedObjects: PropTypes.object, + /** Dictionary containing arrays of updated leaf/group ID's to 'flash' - from Redux store */ + flashItems: PropTypes.shape({ + leaves: PropTypes.arrayOf(PropTypes.number), + groups: PropTypes.arrayOf(PropTypes.number) + }), + /** Dictionary of filter matches */ + filters: PropTypes.object, +} 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..dd6704b0 --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Group.js @@ -0,0 +1,120 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Leaf from './Leaf'; +import {Card, CardText, CardHeader} from 'material-ui/Card'; +import tabularStyle from '../../../styles/tabular'; + +/** Stateless functional component that displays one group in the tabular edit mode. Recursively mounts nested groups and leaves. */ +const Group = (props) => { + const { activeGroup } = props; + const { Leafs, Groups, Rectos, Versos } = props.project + const { + selectedObjects, + filters, + defaultAttributes, + visibleAttributes, + flashItems + } = props.collationManager; + const isActive = selectedObjects.members.includes(activeGroup.id); + const isFiltered = filters.Groups.includes(activeGroup.id); + const groupsOfMatchingElements = filters.GroupsOfMatchingLeafs + filters.GroupsOfMatchingSides + filters.GroupsOfMatchingNotes; + const isAffectedFiltered = groupsOfMatchingElements.includes(activeGroup.id) && !isFiltered; + const hideOthers = filters.hideOthers; + const isFilterActive = filters.active; + // Populate all the members of this Group. + let groupMembers = []; + activeGroup.memberIDs.forEach((memberID, index) => { + if (memberID.charAt(0)==="L"){ + let current_leaf = Leafs[memberID]; + groupMembers.push( + + ); + } else { + let current_group = Groups[memberID]; + groupMembers.push( + + ); + } + }); + + + let attributes = []; + for (var i in defaultAttributes.group) { + let attributeName = defaultAttributes.group[i].name; + if (visibleAttributes.group[attributeName]) { + attributes.push( +
+
+ {defaultAttributes.group[i].displayName} + {activeGroup[attributeName]} +
+
+ ); + } + } + + let activeGroupStyle = {borderColor:"white"}; + if (isActive) { + activeGroupStyle["backgroundColor"] = "#4ED6CB"; + activeGroupStyle["borderColor"] = "#4ED6CB"; + } + if (isFiltered && !hideOthers) { + activeGroupStyle["borderColor"] = "#0f7fdb"; + } + if (isAffectedFiltered && hideOthers && isFilterActive){ + activeGroupStyle["backgroundColor"] = "#d9dbdb"; + activeGroupStyle["borderColor"] = "#d9dbdb"; + } + let groupComponent = + props.handleObjectClick(activeGroup, event)} + style={tabularStyle.group.cardHeader} + > +
+
+ Group {activeGroup.order} +
+ {attributes} +
+
+ + + {groupMembers} + +
+ + if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) + groupComponent = ; + + return ( + groupComponent + ); +} +Group.propTypes = { + /** Group object */ + activeGroup: PropTypes.object, + /** Callback for handling clicking on an object (group or leaf) */ + handleObjectClick: PropTypes.func, +} + +export default Group; 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..b2398ce4 --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Leaf.js @@ -0,0 +1,130 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Card} from 'material-ui/Card'; +import tabularStyle from '../../../styles/tabular'; +import Side from './Side'; + +/** Stateless functional component that displays one leaf in the tabular edit mode. */ + +const Leaf = (props) => { + const { activeLeaf } = props; + const { + selectedObjects, + filters, + defaultAttributes, + visibleAttributes, + 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 (visibleAttributes.side[attributeName]) { + sideAttributesActive = true; + break; + } + } + + // Render any visible leaf attributes + for (let leafAttribute of defaultAttributes.leaf) { + let attributeName = leafAttribute.name; + if (visibleAttributes.leaf[attributeName]) { + let divStyle = "attribute "; + if (isActive) divStyle += "active "; + leafAttributes.push( +
+
+ {leafAttribute.displayName} + {activeLeaf[attributeName]} +
+
+ ); + } + } + + + + let activeLeafStyle = {borderColor: "white"}; + if (isActive) { + activeLeafStyle["backgroundColor"] = "#4ED6CB"; + activeLeafStyle["borderColor"] = "#4ED6CB"; + } + 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 leafComponent = +
+
props.handleObjectClick(activeLeaf, event)} + style={{ ...activeLeafStyle }} + > +
+ Leaf {activeLeaf.order} +
+ {leafAttributes.length>0? +
+ {leafAttributes} +
+ :""} +
+ {sideComponents} +
+
+ + if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) + leafComponent = ; + + return ( + leafComponent + ); +} +Leaf.propTypes = { + /** Leaf object */ + activeLeaf: PropTypes.object, + /** Callback for handling clicking on an object (group or leaf) */ + handleObjectClick: PropTypes.func, +} + +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..a29d75b1 --- /dev/null +++ b/viscoll-app/src/components/collationManager/tabularMode/Side.js @@ -0,0 +1,83 @@ +import React from 'react'; + + +const Side = (props) => { + const { activeSide } = props; + const { + selectedObjects, + filters, + defaultAttributes, + visibleAttributes, + } = 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) { + let attributeName = attribute.name; + if (visibleAttributes.side[attributeName]) { + sideAttributes.push( +
+ {attribute.displayName}: {activeSide[attributeName]} +
+ ); + } + } + + let activeSideStyle = {}; + + if (isActive) { + activeSideStyle["backgroundColor"] = "#4ED6CB"; + activeSideStyle["borderColor"] = "#4ED6CB"; + } + + if (isFiltered && !hideOthers) { + activeSideStyle["borderColor"] = "#0f7fdb"; + } + + if (isAffectedFiltered && hideOthers && isFilterActive){ + activeSideStyle["backgroundColor"] = "#d9dbdb"; + activeSideStyle["borderColor"] = "#d9dbdb"; + } + + const activeSideName = activeSide.id.split("_")[0]; + let sideComponent = ( +
props.handleObjectClick(activeSide, event)} + style={{ ...activeSideStyle }} + > + {activeSideName.charAt(0)} +
+ ); + + if (sideAttributes.length>0) { + sideComponent = ( +
props.handleObjectClick(activeSide, event)} + style={{ ...activeSideStyle }} + > +
{activeSideName}
+ {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..07ce2f0b --- /dev/null +++ b/viscoll-app/src/components/dashboard/CloneProject.js @@ -0,0 +1,66 @@ +import React from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; + + +export default class CloneProject extends React.Component { + + constructor(props) { + super(props); + this.state = { + projectIndex: -1 + } + } + + onChange = (event, projectIndex) => { + this.setState({ projectIndex }); + } + + submit = (event) => { + event.preventDefault(); + this.props.cloneProject(this.props.allProjects[this.state.projectIndex].id); + this.props.reset(); + this.props.close(); + } + + render(){ + return ( +
+

Clone Existing Collation

+
+ + {this.props.allProjects.map((project, index)=>{ + return ( + + ); + })} + +
+ + +
+
+
+ ); + } +} diff --git a/viscoll-app/src/components/dashboard/EditProjectForm.js b/viscoll-app/src/components/dashboard/EditProjectForm.js new file mode 100644 index 00000000..2d2b681b --- /dev/null +++ b/viscoll-app/src/components/dashboard/EditProjectForm.js @@ -0,0 +1,395 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +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'; + +/** + * 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, + editing: { + title: false, + shelfmark: false, + date: false, + }, + }; + } + + /** + * Update project pane if a new project was selected in the dashboard + * @param {object} nextProps + * @public + */ + 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, + selectedProjectIndex: nextProps.selectedProjectIndex, + }) + } + } + + /** + * Validate user input and display appropriate error message + * @param {string} type text field name + * @public + */ + checkValidationError = (type) => { + const errors = {}; + const allProjectsExceptCurrent = [...this.state.allProjects]; + allProjectsExceptCurrent.splice(this.state.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 + * @public + */ + 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 + * @param {string} event + * @param {string} newValue new value + * @param {string} type text field name + * @public + */ + onInputChange = (event, newValue, type) => { + this.setState({[type]: newValue, editing: {...this.state.editing, [type]:true}}, () => { + this.setState({errors: this.checkValidationError(type)}) + }); + } + + /** + * Toggle delete confirmation dialog + * @param {boolean} deleteDialog show dialog? + * @public + */ + handleDeleteDialogToggle = (deleteDialog=false) => { + this.setState({ deleteDialog }); + }; + + /** + * Toggle unsaved changes dialog + * @param {boolean} unsavedDialog show dialog? + * @public + */ + handleUnsavedDialogToggle = (unsavedDialog=false) => { + this.setState({ unsavedDialog }); + }; + + /** + * Submit project update of a specific input field + * @param {object} event + * @param {string} field text field name + * @public + */ + handleProjectUpdate = (event, field) => { + 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 + * @public + */ + handleProjectDelete = () => { + this.props.closeProjectPanel(); + this.setState({deleteDialog: false}); + const projectID = this.props.selectedProject.id; + const user = { + id: this.props.user.id, + token: this.props.user.token + }; + this.props.deleteProject(projectID, user); + }; + + /** + * Close project panel + * @param {boolean} ignoreChanges show ignore changes dialog? + * @public + */ + 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 + * @public + */ + isEditing = () => { + return (this.state.editing.title||this.state.editing.shelfmark||this.state.editing.uri||this.state.editing.date); + } + + /** + * Reset text field to original values + * @param {string} type "project" + * @param {string} field text field name + * @public + */ + 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 + * @param {string} field text field name + * @public + */ + submitButtons = (field) => { + if (this.state.editing[field]) { + return ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + name="submit" + type="submit" + disabled={this.ifErrorsExist()} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.handleProjectCancelUpdate(field)} + /> +
+ ) + } else { + return ""; + } + } + + render() { + const selectedProject = this.props.selectedProject; + if (!selectedProject) + return
; + + + let projectPanelData = ( +
+
this.handleProjectUpdate(e, "title")}> + this.onInputChange(event, newValue, "title")} + floatingLabelStyle={{fontSize: 25}} + fullWidth={true} + /> + {this.submitButtons("title")} + +
this.handleProjectUpdate(e, "shelfmark")}> + this.onInputChange(event, newValue, "shelfmark")} + floatingLabelStyle={{fontSize: 25}} + fullWidth={true} + /> + {this.submitButtons("shelfmark")} + +
this.handleProjectUpdate(e, "date")}> + this.onInputChange(event, newValue, "date")} + floatingLabelStyle={{fontSize: 25}} + fullWidth={true} + hintText="N/A" + /> + {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()} + />, + , + ]; + + const unsaveActions = [ + this.handleUnsavedDialogToggle()} + />, + this.handleProjectPanelClose(true)} + />, + ]; + + + return ( +
+ + + this.handleProjectPanelClose()} /> + + + {projectPanelData} + +
+ + this.props.history.push(`/project/${this.props.selectedProject.id}`)} + secondary + style={{width:"49%",float:"left",marginRight:"2%"}} + /> + this.handleDeleteDialogToggle(true)} + labelColor="#D87979" + style={{width:"49%"}} + /> + + + + + +
+ ); + } + static propTypes = { + /** Array of projects belonging to the user. */ + allProjects: PropTypes.array, + /** Currently selected project object. */ + selectedProject: PropTypes.shape({ + created_at: PropTypes.string, + updated_at: PropTypes.string, + id: PropTypes.string, + title: PropTypes.string, + }), + /** Index of the selected project in the list of projects belonging to the user. */ + selectedProjectIndex: PropTypes.number, + /** Callback to close the project panel. */ + closeProjectPanel: PropTypes.func, + /** Callback to update a project. */ + updateProject: PropTypes.func, + /** Callback to delete a project. */ + deleteProject: PropTypes.func, + /** User object */ + user: PropTypes.object, + + } +} + + + +export default EditProjectForm; diff --git a/viscoll-app/src/components/dashboard/EditProjectForm.md b/viscoll-app/src/components/dashboard/EditProjectForm.md new file mode 100644 index 00000000..3c9a82ea --- /dev/null +++ b/viscoll-app/src/components/dashboard/EditProjectForm.md @@ -0,0 +1,9 @@ + +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| unsavedDialog | boolean | The dialog to alert of unsaved changes will appear if this variable is set to `true` | +| deleteDialog | boolean | The dialog confirm project deletion will appear if this variable is set to `true` | +| editing | object | Is `true` if there are unsaved changes in any input fields
title: boolean
shelfmark: boolean
uri: boolean
date: boolean| + diff --git a/viscoll-app/src/components/dashboard/ImportProject.js b/viscoll-app/src/components/dashboard/ImportProject.js new file mode 100644 index 00000000..05db7ada --- /dev/null +++ b/viscoll-app/src/components/dashboard/ImportProject.js @@ -0,0 +1,144 @@ +import React from 'react'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import TextField from 'material-ui/TextField'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; +import Dropzone from 'react-dropzone' + + +export default class ImportProject extends React.Component { + + constructor(props) { + super(props); + this.state = { + importData: "", + importFormat: "xml", + } + } + + + isDisabled = () => { + if (this.state.importData) + return (this.state.importData.length===0); + else + return true + } + + submit = (event) => { + event.preventDefault(); + if (!this.isDisabled()) { + this.props.importProject({importData: this.state.importData, importFormat: this.state.importFormat}); + } + } + + onChange = (value, type) => { + this.setState({ [type]: value }); + } + + componentWillReceiveProps = (nextProps) => { + if (nextProps.importStatus==="SUCCESS"){ + nextProps.reset(); + nextProps.close(); + } + } + + + checkIfFileTypeIsInvalid = (file) => { + const allowedFileTypes = ["json", "xml", "txt"]; + 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}) + } + + render() { + const dropFileText =

+ Drop a file here, or click to select a file to upload.
+ Only *.json, .xml and *.txt files will be accepted. +

; + return ( +
+
+

Import

+
+

In the textbox below, please paste the content of your exported collation data.

+
+ this.onChange(v, "importData")} + underlineShow={false} + style={{border: "1px solid #cccccc", width: "99%"}} + textareaStyle={{padding:"0px 15px"}} + /> +
+ {this.handleFileSelected(accepted)}} + accept=".json, .xml, .txt" + multiple={false} + > + {dropFileText} + +

Import format:

+ this.onChange(v, "importFormat")} + > + + + + +
+ {this.props.importStatus} +
+ + +
+ +
+ ); + }; +} + + + + // + // + // diff --git a/viscoll-app/src/components/dashboard/ListView.js b/viscoll-app/src/components/dashboard/ListView.js new file mode 100644 index 00000000..30375069 --- /dev/null +++ b/viscoll-app/src/components/dashboard/ListView.js @@ -0,0 +1,59 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Table, + TableBody, + TableHeader, + TableHeaderColumn, + TableRow, + TableRowColumn, +} from 'material-ui/Table'; + +/** + * List the projects in a table format + */ +const ListView = ({singleClickIndex, selectProject, allProjects=[], doubleClick}) => { + + const projectsList = allProjects.map((project, i) => { + var selected = singleClickIndex === i; + return ( + doubleClick(project.id)} + selected={selected} + style={{background:"rgba(255,255,255,0.2)", cursor:"pointer"}} + > + {project.title} + {new Date(project.updated_at).toLocaleString('en-US')} + + ); + }); + return ( + {selectProject(index[0])}} + > + + + Name + Date Modified + + + + {projectsList} + +
+ ); +}; +ListView.propTypes = { + /** Index of project that was selected through singleclick by user */ + singleClickIndex: PropTypes.number, + /** Callback used when user selects a project */ + selectProject: PropTypes.func, + /** Array of projects belonging to the user */ + allProjects: PropTypes.array, + /** Callback for doubleclicking on a project */ + doubleClick: PropTypes.func, + } +export default ListView; diff --git a/viscoll-app/src/components/dashboard/NewProjectContainer.js b/viscoll-app/src/components/dashboard/NewProjectContainer.js new file mode 100644 index 00000000..a46aa4d4 --- /dev/null +++ b/viscoll-app/src/components/dashboard/NewProjectContainer.js @@ -0,0 +1,281 @@ +import React from 'react'; +import Dialog from 'material-ui/Dialog'; +import NewProjectSelection from './NewProjectSelection'; +import ProjectDetails from './ProjectDetails'; +import ProjectStructure from './ProjectStructure'; +import ImportProject from './ImportProject'; +import CloneProject from './CloneProject'; + +export default class NewProjectContainer extends React.Component { + constructor(props) { + super(props); + this.state = { + projectType: "", + step: 1, + title: "", + shelfmark: "", + date: "", + quireNo: 1, + leafNo: 10, + conjoined: true, + collationGroups: [], + errors: { + title: "", + shelfmark: "", + date: "", + }, + }; + } + + set = (name, value) => { + this.setState({[name]: value}, () => { + this.setState({errors:{...this.state.errors, ...this.checkValidationError(name)}}); + }); + } + + reset = () => { + this.setState({ + projectType:"", + step:1, + title: "", + shelfmark: "", + date: "", + quireNo: 1, + leafNo: 10, + conjoined: true, + collationGroups: [], + errors: { + title: "", + shelfmark: "", + date: "", + }, + }); + } + + + + /** + * Validate user input and display appropriate error message + * @param {string} type text field name + * @public + */ + checkValidationError = (type) => { + const errors = {}; + this.props.allProjects.forEach(project => { + if (type==="title") { + if (project.title === this.state.title) { + errors.title = "Project title should be unique"; + } else if (!this.state.title) { + errors.title = "Project title is required"; + } + } else if (type==="shelfmark") { + if (project.shelfmark === this.state.shelfmark) { + errors.shelfmark = "Manuscript shelfmark should be unique"; + } else if (!this.state.shelfmark) { + errors.shelfmark = "Manuscript shelfmark is required"; + } + } + }); + if (Object.keys(errors).length===0) { + errors[type] = ""; + } + return errors; + } + /** + * Return true if any errors exist in the project form + * @public + */ + doErrorsExist = () => { + if (this.state.errors.title) + return true; + if (this.state.errors.shelfmark) + return true; + for (let group in this.state.collationGroups) { + if (!this.state.collationGroups[group].leaves && this.state.step===2) return true; + } + if (!this.state.title && this.state.step===1) + return true; + if (!this.state.shelfmark && this.state.step === 1) + return true; + return false; + } + + /** + * Remove a group + * @param {number} groupNo group number + * @public + */ + handleRemoveCollationGroupRow = (groupNo) => { + let newCollationGroups = []; + this.state.collationGroups.forEach((group) => { + if (group.number < groupNo) { + newCollationGroups.push(group); + } else if (group.number > groupNo) { + newCollationGroups.push({...group, number: group.number-1}) + } + }); + this.setState({ collationGroups: newCollationGroups }); + } + + /** + * Add a new group + * @public + */ + handleAddNewCollationGroupRow = () => { + let newCollationGroups = [].concat(this.state.collationGroups); + for (let i=0; i { + let newCollationGroups = []; + this.state.collationGroups.forEach((group, i) => { + if (updatedGroup.number === i+1) { + updatedGroup = {...group}; + if (field==="oddLeaf") { + updatedGroup[field] = value; + } else if (field==="leaves"){ + updatedGroup[field] = parseInt(event.target.value, 10); + if (updatedGroup[field] < updatedGroup["oddLeaf"]) { + updatedGroup["oddLeaf"] = 1; + } + if (updatedGroup[field]===1) { + updatedGroup["conjoin"]=false; + } + } + newCollationGroups.push(updatedGroup); + } else { + newCollationGroups.push(group); + } + }); + this.setState({ collationGroups: newCollationGroups }); + } + + /** + * Toggle the conjoin option for a specific group + * @param {object} updatedGroup + * @public + */ + handleToggleConjoin = (updatedGroup) => { + let newCollationGroups = []; + this.state.collationGroups.forEach((group, i) => { + if (updatedGroup.number === i+1) { + updatedGroup = {...group}; + updatedGroup.conjoin = !updatedGroup.conjoin; + newCollationGroups.push(updatedGroup); + } else { + newCollationGroups.push(group); + } + }); + this.setState({ collationGroups: newCollationGroups }); + } + + + finish = () => { + const user = { + id: this.props.user.id, + token: this.props.user.token + }; + let request = { + project: { + title: this.state.title, + shelfmark: this.state.shelfmark, + metadata: { + date: this.state.date + } + }, + groups: [] + } + this.state.collationGroups.forEach((group)=>request.groups.push(group)); + this.props.createProject(request, user); + this.reset(); + this.props.close(); + } + + handleRequestClose = () => { + if (this.state.step===1) { + this.reset(); + this.props.close(); + } + } + + + render() { + let content = this.set("projectType",type)} + />; + if (this.state.projectType==="new") { + if (this.state.step===1) { + content = this.set("step", 2)} + previousStep={this.reset} + doErrorsExist={this.doErrorsExist} + />; + } else { + content = this.set("step", 1)} + set={this.set} + quireNo={this.state.quireNo} + leafNo={this.state.leafNo} + conjoined={this.state.conjoined} + collationGroups={this.state.collationGroups} + handleToggleConjoin={this.handleToggleConjoin} + onInputChangeCollationGroupsRows={this.onInputChangeCollationGroupsRows} + addCollationRows={this.handleAddNewCollationGroupRow} + handleRemoveCollationGroupRow={this.handleRemoveCollationGroupRow} + />; + } + } else if (this.state.projectType==="import") { + content = ( + + ); + } + else if (this.state.projectType==="clone") { + content = ( + + ); + } + return ( +
+ this.handleRequestClose()} + className="newProjectDialog" + autoScrollBodyContent + > + {content} + +
+ ); + } +} diff --git a/viscoll-app/src/components/dashboard/NewProjectSelection.js b/viscoll-app/src/components/dashboard/NewProjectSelection.js new file mode 100644 index 00000000..9c47f1fb --- /dev/null +++ b/viscoll-app/src/components/dashboard/NewProjectSelection.js @@ -0,0 +1,74 @@ +import React from 'react'; +import {Card, CardText} from 'material-ui/Card'; +import IconButton from 'material-ui/IconButton'; +import AddIcon from 'material-ui/svg-icons/content/add'; +import CopyIcon from 'material-ui/svg-icons/content/content-copy'; +import ImportIcon from 'material-ui/svg-icons/action/system-update-alt'; + +const NewProjectSelection = (props) => { + return ( +
+ props.setProjectType("new")} + > + +
+
+ + + +
+
+

Create new

+

Create a new collation from scratch

+
+
+
+
+
+ props.setProjectType("import")} + > + +
+
+ + + +
+
+

Import

+

Import a collation from VisColl XML, JSON or formula

+
+
+
+
+ +
+ props.setProjectType("clone")} + > + +
+
+ + + +
+
+

Clone existing

+

Clone one of your existing collations

+
+
+
+
+
+ ); +} +export default NewProjectSelection; \ No newline at end of file diff --git a/viscoll-app/src/components/dashboard/ProjectDetails.js b/viscoll-app/src/components/dashboard/ProjectDetails.js new file mode 100644 index 00000000..0960a1c3 --- /dev/null +++ b/viscoll-app/src/components/dashboard/ProjectDetails.js @@ -0,0 +1,56 @@ +import React from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; + + + +const ProjectDetails = (props) => { + let submit = (event) => { + event.preventDefault(); + if(!props.doErrorsExist()) props.nextStep() + } + return ( +
+

Object Details

+
+
+ props.set("title", newValue)} + fullWidth + /> + props.set("shelfmark", newValue)} + fullWidth + /> + props.set("date", newValue)} + fullWidth + /> +
+
+ + +
+
+
+ ); +} +export default ProjectDetails; diff --git a/viscoll-app/src/components/dashboard/ProjectStructure.js b/viscoll-app/src/components/dashboard/ProjectStructure.js new file mode 100644 index 00000000..db1d4d5f --- /dev/null +++ b/viscoll-app/src/components/dashboard/ProjectStructure.js @@ -0,0 +1,175 @@ +import React from 'react'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import Checkbox from 'material-ui/Checkbox'; +import { + Table, + TableBody, + TableHeader, + TableHeaderColumn, + TableRow, + TableRowColumn, +} from 'material-ui/Table'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import IconButton from 'material-ui/IconButton'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; + +const ProjectStructure = (props) => { + + /** + * Return a list of MenuItem's for the unconjoined drop down menu + * @param {number} selectedValue + * @param {array} unconjoinLeafsList + * @public + */ + let menuItems = (selectedValue, unconjoinLeafsList, isDisabled) => { + if (isDisabled) { + return () + } + return unconjoinLeafsList.map((val) => ( + + )); + } + + const collationGroupsRows = []; + props.collationGroups.forEach((group) => { + const unconjoinLeafsList = !group.leaves? [] : Array.from(Array(group.leaves).keys()); + collationGroupsRows.push( + + {group.number} + + props.onInputChangeCollationGroupsRows(e, group, "leaves")} + style={{width:50}} + /> + + + props.handleToggleConjoin(group)} + checked={group.conjoin} + disabled={group.leaves<=1} + style={{marginLeft:8}} + /> + + + {props.onInputChangeCollationGroupsRows(e, group, "oddLeaf", value)}} + disabled={(!group.conjoin || group.leaves%2 === 0)} + > + {menuItems(group.oddLeaf, unconjoinLeafsList, (!group.conjoin || group.leaves%2 === 0))} + + + + props.handleRemoveCollationGroupRow(group.number)} + > + + + + + ); + }); + + return ( +
+

Structure

+
+
+ # of Quires +
+
+ {props.set("quireNo", parseInt(newValue, 10))}} + style={{width:50}} + type="number" + /> +
+
+ × + # of Leaves +
+
+ {props.set("leafNo", parseInt(newValue, 10))}} + style={{width:50}} + type="number" + min="1" + /> +
+
+ props.set("conjoined", !props.conjoined)} + /> +
+
+ +
+
+ {collationGroupsRows.length>0? +
+ + + + Quire no. + Number of leaves + Conjoin + Unconjoined leaf + + + + {collationGroupsRows} + +
+
: +
+ You can pre-populate your collation with quires and leaves by using the formula above. + Generate the groups and leaves by clicking the "Add" button. You can add multiple times. +
+ } + +
+ + +
+
+ ); +} +export default ProjectStructure; diff --git a/viscoll-app/src/components/export/Export.js b/viscoll-app/src/components/export/Export.js new file mode 100644 index 00000000..f4fb6978 --- /dev/null +++ b/viscoll-app/src/components/export/Export.js @@ -0,0 +1,65 @@ +import React from 'react'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import fileDownload from 'js-file-download'; +import copy from 'copy-to-clipboard'; + + + +const Export = (props) => { + + const filename = props.projectTitle.replace(/\s/g, "_"); + + let actions = [ + fileDownload(props.exportedData, `${filename}.${props.exportedType}`)} + />, + { + copy(props.exportedData); + props.showCopyToClipboardNotification(); + }} + />, + props.handleExportToggle(false)} + />, + ]; + + let verticalOverflow; + if (props.exportedType==="formula"){ + actions.shift(); + verticalOverflow = "hidden"; + } + let message = "This JSON export contains the complete collation with all of its data"; + if (props.exportedType==="xml") { + message = "This XML export does not fully export all the collation data"; + } + + return ( + props.handleExportToggle(false)} + contentStyle={{maxWidth: 1000}} + > + {message} +
+
+          {props.exportedData}
+        
+
+
+ ); +} + + +export default Export; + diff --git a/viscoll-app/src/components/filter/FilterRow.js b/viscoll-app/src/components/filter/FilterRow.js new file mode 100644 index 00000000..c7823679 --- /dev/null +++ b/viscoll-app/src/components/filter/FilterRow.js @@ -0,0 +1,205 @@ +import React, {Component} from 'react'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import TextField from 'material-ui/TextField'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import ContentAdd from 'material-ui/svg-icons/content/add'; +import IconButton from 'material-ui/IconButton'; +import FloatingActionButton from 'material-ui/FloatingActionButton'; +import AutoComplete from 'material-ui/AutoComplete'; + +/** A row of filter query */ +class FilterRow extends Component { + + renderAttributeMenuItems = () => { + if (this.props.type) { + return this.props.defaultAttributes[this.props.type].map(this.mapAttributeMenuItems); + } else { + return null; + } + } + mapNoteAttributeMenuItems = (noteType, index) => { + return + } + mapAttributeMenuItems = (item, index) => { + return + } + renderValueItems = (item, index) => { + return -1} />; + } + + mapConditionItems = (item) => { + return + } + + filterConditionItems = (item) => { + let isDropdown = false; + try { + isDropdown = (this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['isDropdown']); + } catch (e) { } + return ((!isDropdown && item && !item.includes("equal"))|| (isDropdown && item && !item.includes("contain"))); + } + + renderValueField = () => { + let input =; + if (this.props.attributeIndex!=="") { + try { + if (this.props.defaultAttributes[this.props.type] && this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['options']!==undefined) { + input = + (this.props.onChange(this.props.queryIndex,"values",e,i,v)} + multiple + errorText={(this.props.type!==null && this.props.values.length===0)?"Required":""} + style={{width:'100%'}} + > + {this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['options'].map(this.renderValueItems)} + ); + } + else if (this.props.defaultAttributes[this.props.type] && this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['name']==="conjoined_leaf_order"){ + const dataSourceConfig = { + text: 'textKey', + value: 'valueKey', + }; + input = + (this.props.onChange(this.props.queryIndex, "values", null, null, v, s)} + filter={AutoComplete.caseInsensitiveFilter} + dataSource={this.props.conjoinedToAutoComplete} + dataSourceConfig={dataSourceConfig} + listStyle={{ maxHeight: 300, overflow: 'auto' }} + openOnFocus={true} + style={{width:'100%'}} + />); + } + else if (this.props.defaultAttributes[this.props.type] && this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['name']==="type"){ + const dataSourceConfig = { + text: 'textKey', + value: 'valueKey', + }; + let dataSource = this.props.noteTypes.map((noteType) => { + return {textKey: noteType, valueKey: noteType} + }) + input = + (this.props.onChange(this.props.queryIndex, "values", null, null, v, s)} + filter={AutoComplete.caseInsensitiveFilter} + dataSource={dataSource} + dataSourceConfig={dataSourceConfig} + listStyle={{ maxHeight: 300, overflow: 'auto' }} + openOnFocus={true} + style={{width:'100%'}} + />); + } + else { + input = + this.props.onChange(this.props.queryIndex,"values",e,0,[v])} + />; + } + } catch (e) {} + } + return input; + } + + renderRow = () => { + let row = +
+
+ {let queryIndex = this.props.queryIndex; this.props.onChange(queryIndex,"type",e,i,v);}} + style={{width:'100%'}} + disabled={this.props.disableNewRow} + > + + + + + +
+ +
+ {let queryIndex = this.props.queryIndex; this.props.clearFilterRowOnAttribute(queryIndex, v, i); this.props.onChange(this.props.queryIndex,"attribute",e,i,v)}} + style={{width:'100%'}} + errorText={(this.props.type!==null && this.props.attribute==="")?"Required":""} + autoWidth + disabled={this.props.disableNewRow} + > + {this.renderAttributeMenuItems()} + +
+ +
+ this.props.onChange(this.props.queryIndex,"condition",e,i,v)} + style={{width:'100%'}} + errorText={(this.props.type!==null && this.props.condition==="")?"Required":""} + disabled={this.props.disableNewRow} + > + {['equals', 'contains', 'not equals', 'not contains'].filter((item)=>this.filterConditionItems(item)).map(this.mapConditionItems)} + +
+ +
+ {this.renderValueField()} +
+ +
+ this.props.onChange(this.props.queryIndex,"conjunction",e,i,v)} + style={{width:'100%'}} + disabled={this.props.lastRow} + errorText={(!this.props.lastRow && this.props.conjunction==="")?"Required":""} + > + + + +
+ +
+ this.props.removeRow(this.props.queryIndex)} + style={(this.props.queryIndex===0 && this.props.queriesLength===1)? {opacity:0,pointerEvents:'none'}: {}} + > + + + this.props.addRow()} + style={(this.props.queryIndex===this.props.queriesLength-1)? {marginLeft:10} : {opacity:0,pointerEvents:'none',marginLeft:10}} + secondary + disabled={this.props.disableAddRow} + > + + +
+
+ return row; + } + + + render() { + return this.renderRow(); + } + +} +export default FilterRow; diff --git a/viscoll-app/src/components/global/AppLoadingScreen.js b/viscoll-app/src/components/global/AppLoadingScreen.js new file mode 100644 index 00000000..3bc35538 --- /dev/null +++ b/viscoll-app/src/components/global/AppLoadingScreen.js @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import logoImg from '../../assets/logo_white.png'; +import CircularProgress from 'material-ui/CircularProgress'; + +/** Stateless functional component for the app loading screen */ +const AppLoadingScreen = (props) => { + const logo = logo ; + if (props.loading) { + return
+
+
+ {logo} +
+
+ +
+
+
; + } else { + return
+ } +} +AppLoadingScreen.propTypes = { + /** `true` if loading screen should be shown */ + loading: PropTypes.bool, +} +export default AppLoadingScreen; diff --git a/viscoll-app/src/components/global/ImageViewer.js b/viscoll-app/src/components/global/ImageViewer.js new file mode 100644 index 00000000..160a85ae --- /dev/null +++ b/viscoll-app/src/components/global/ImageViewer.js @@ -0,0 +1,92 @@ +import React from 'react'; +import OpenSeadragon from 'openseadragon'; +import UUID from 'uuid'; +import BlankPage from '../../assets/blank_page.png'; + +/** iamge viewing component (OpenSeaDragon) */ +export default class ImageViewer extends React.Component { + constructor(props) { + super(props); + this.state = { + suffixedID: 'openseadragon-' + UUID.v4(), + osd: null + } + } + + componentDidMount() { + var tilesSources = []; + if (this.props.rectoURL) tilesSources.push(this.props.rectoURL + "/info.json"); + if (this.props.versoURL) tilesSources.push(this.props.versoURL + "/info.json"); + if (!this.props.rectoURL && !this.props.versoURL) tilesSources = [{type: "image", url: BlankPage}]; + this.setState({ + osd: OpenSeadragon({ + id: this.state.suffixedID, + prefixUrl: 'https://cdnjs.cloudflare.com/ajax/libs/openseadragon/2.3.1/images/', + showNavigationControl: true, + showFullPageControl: false, + showRotationControl: true, + showNavigator: true, + collectionMode: true, + collectionRows: 1, + collectionTileMargin: 1, + crossOriginPolicy: 'Anonymous', + navImages: { + zoomIn: { + REST: "zoomin_rest.png", + GROUP: "zoomin_grouphover.png", + HOVER: "zoomin_hover.png", + DOWN: "zoomin_pressed.png" + }, + zoomOut: { + REST: "zoomout_rest.png", + GROUP: "zoomout_grouphover.png", + HOVER: "zoomout_hover.png", + DOWN: "zoomout_pressed.png" + }, + home: { + REST: "home_rest.png", + GROUP: "home_grouphover.png", + HOVER: "home_hover.png", + DOWN: "home_pressed.png" + }, + rotateleft: { + REST: "rotateleft_rest.png", + GROUP: "rotateleft_grouphover.png", + HOVER: "rotateleft_hover.png", + DOWN: "rotateleft_pressed.png" + }, + rotateright: { + REST: "rotateright_rest.png", + GROUP: "rotateright_grouphover.png", + HOVER: "rotateright_hover.png", + DOWN: "rotateright_pressed.png" + } + }, + tileSources: tilesSources + }) + }); + } + + componentWillUpdate(nextProps) { + this.addTiles(nextProps.rectoURL, nextProps.versoURL); + } + + addTiles(r, v) { + if (this.state.osd) { + var tilesSources = []; + if (r) tilesSources.push(r + "/info.json"); + if (v) tilesSources.push(v + "/info.json"); + if (!r && !v) tilesSources = [{type: "image", url: BlankPage}]; + this.state.osd.open(tilesSources); + } + } + + render() { + let style = {width: "100%", height: "500px", background: "black"}; + if (this.props.fixed) style = {position: "fixed", width:"42.5%",height:"82%", left: "30%", background: 'black', padding: 5}; + return ( +
+
+ ); + } +} diff --git a/viscoll-app/src/components/global/LoadingScreen.js b/viscoll-app/src/components/global/LoadingScreen.js new file mode 100644 index 00000000..de2db68d --- /dev/null +++ b/viscoll-app/src/components/global/LoadingScreen.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from 'material-ui/Dialog'; +import loadingImg from '../../assets/viscoll_loading.gif'; + +/** Stateless functional component for the loading screen */ +const LoadingScreen = (props) => { + const logo = logo ; + return ( + + {logo} + + ); +} +LoadingScreen.propTypes = { + /** `true` if loading screen should be shown */ + loading: PropTypes.bool, +} +export default LoadingScreen; diff --git a/viscoll-app/src/components/global/Notification.js b/viscoll-app/src/components/global/Notification.js new file mode 100644 index 00000000..3391d53d --- /dev/null +++ b/viscoll-app/src/components/global/Notification.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Snackbar from 'material-ui/Snackbar'; + +/** Stateless functional component for snackbar notification */ +const Notification = (props) => { + return ( + + ); +} +export default Notification; diff --git a/viscoll-app/src/components/global/PageNotFound.js b/viscoll-app/src/components/global/PageNotFound.js new file mode 100644 index 00000000..611a967e --- /dev/null +++ b/viscoll-app/src/components/global/PageNotFound.js @@ -0,0 +1,19 @@ +import React, { Component } from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; + +export default class PageNotFound extends Component { + render() { + return
+
+

404!

+

Well, this isn't where you parked your car.

+ + this.props.history.push("/dashboard")} + /> +
+
+ } +} \ No newline at end of file diff --git a/viscoll-app/src/components/global/Panel.js b/viscoll-app/src/components/global/Panel.js new file mode 100644 index 00000000..8f302f1b --- /dev/null +++ b/viscoll-app/src/components/global/Panel.js @@ -0,0 +1,33 @@ +import React, {Component} from 'react'; +import sidebarStyle from "../../styles/sidebar"; +import {Card, CardText, CardHeader} from 'material-ui/Card'; + +/** Expandable panel component for the project sidebar. Panel examples: Filter, export.. */ +export default class Panel extends Component { + render() { + return ( + + + + {this.props.children} + + + ) + } + +} diff --git a/viscoll-app/src/components/imageManager/AddManifest.js b/viscoll-app/src/components/imageManager/AddManifest.js new file mode 100644 index 00000000..03ef322d --- /dev/null +++ b/viscoll-app/src/components/imageManager/AddManifest.js @@ -0,0 +1,90 @@ +import React, {Component} from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; +import TextField from 'material-ui/TextField'; + +export default class AddManifest extends Component { + constructor(props) { + super(props); + this.state = { + url: "", + urlError: "" + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.createManifestError!==""){ + if (this.state.urlError==="") + this.setState({urlError: nextProps.createManifestError}); + } else { + this.setState({url: "", urlError: ""}); + } + } + + onChange = (type, value) => { + this.setState({[type]: value}, ()=>{this.runValidation()}) + } + + onSubmit = (e) => { + e.preventDefault(); + const manifest = {url: this.state.url} + this.props.createManifest({manifest}); + } + + onCancel = (e) => { + this.setState({url: "", urlError: ""}) + this.props.cancelCreateManifest(); + } + + runValidation = () => { + for (const manifestID in this.props.manifests){ + const manifest = this.props.manifests[manifestID]; + if (manifest.url===this.state.url){ + this.setState({urlError: `Manifest with url: ${manifest.url} already exists.`}); + return; + } + } + // No validation errors + this.setState({urlError: ""}); + } + + isValid = () => { + return (this.state.urlError==="" && this.state.url!=="") + } + + render() { + return ( +
this.onSubmit(e)}> +

Add a new Manifest

+
+
URL
+
+ this.onChange("url", v)} + /> +
+
+ +
+ this.onCancel(e)} + style={{marginRight: 5}} + /> + this.onSubmit(e)} + /> +
+
+ ); + } +} diff --git a/viscoll-app/src/components/imageManager/DeleteManifest.js b/viscoll-app/src/components/imageManager/DeleteManifest.js new file mode 100644 index 00000000..32e65576 --- /dev/null +++ b/viscoll-app/src/components/imageManager/DeleteManifest.js @@ -0,0 +1,39 @@ +import React, {Component} from 'react'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; + +export default class DeleteManifest extends Component { + render() { + const actions = [ + , + {this.props.handleClose(); this.props.deleteManifest()}} + backgroundColor="#D87979" + labelColor="#ffffff" + />, + ]; + + if (this.props.open) { + return ( +
+ + +
+ ); + } else { + return
; + } + } +} \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/EditManifest.js b/viscoll-app/src/components/imageManager/EditManifest.js new file mode 100644 index 00000000..102b4c31 --- /dev/null +++ b/viscoll-app/src/components/imageManager/EditManifest.js @@ -0,0 +1,105 @@ +import React, {Component} from 'react'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import TextField from 'material-ui/TextField'; + +export default class EditManifest extends Component { + constructor(props) { + super(props); + this.state = { + name: props.manifest.name, + nameError: "" + } + } + + componentWillReceiveProps(nextProps) { + this.setState({name: nextProps.manifest.name}) + } + + onChange = (type, value) => { + this.setState({[type]: value}, ()=>{this.runValidation()}) + } + + onSubmit = (e) => { + e.preventDefault(); + const manifest = {id: this.props.manifest.id, name: this.state.name.trim()} + this.setState({name: ""}, ()=>{ + this.props.updateManifest({manifest}); + this.props.handleClose(); + }) + } + + onCancel = (e) => { + this.setState({name: ""}) + } + + runValidation = () => { + for (const manifestID in this.props.manifests){ + const manifest = this.props.manifests[manifestID]; + if (manifestID!==this.props.manifest.id && manifest.name===this.state.name.trim()){ + this.setState({nameError: `Manifest with name: ${manifest.name} already exists.`}); + return; + } else if (this.state.name.length>51) { + this.setState({nameError: `Manifest name must be under 50 characters.`}); + return; + } + } + // No validation errors + this.setState({nameError: ""}); + } + + isValid = () => { + return (this.state.nameError==="" && this.state.name!=="" && this.props.manifest.name!==this.state.name.trim()) + } + + render() { + const actions = [ + , + , + ]; + + if (this.props.open) { + return ( +
+ +
+
this.onSubmit(e)}> +
+
Manifest name
+
+ this.onChange("name", v)} + fullWidth + /> +
+
+
+
+
+
+ ); + } else { + return
; + } + } +} \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/ManageManifests.js b/viscoll-app/src/components/imageManager/ManageManifests.js new file mode 100644 index 00000000..ebf47a98 --- /dev/null +++ b/viscoll-app/src/components/imageManager/ManageManifests.js @@ -0,0 +1,106 @@ +import React, {Component} from 'react'; +import FlatButton from 'material-ui/FlatButton'; +import {Card, CardActions} from 'material-ui/Card'; +import AddManifest from './AddManifest'; +import EditManifest from './EditManifest'; +import DeleteManifest from './DeleteManifest'; +import ImageViewer from "../global/ImageViewer"; +import Dialog from 'material-ui/Dialog'; + +class ManageManifests extends Component { + constructor(props) { + super(props); + this.state = { + editOpen: false, + deleteOpen: false, + openManifest: {id: "", name: "", url: ""}, + imageModalOpen: false, + activeImage: null + }; + } + handleOpen = (name, openManifest) => { + this.setState({[name]: true, openManifest}); + }; + + handleClose = (name) => { + this.setState({[name]: false}); + }; + + toggleImageModal = (imageModalOpen, activeImage) => { + this.setState({imageModalOpen, activeImage}) + } + + renderManifest = (manifestID) => { + const manifest = this.props.manifests[manifestID] + return ( +
+ +
+
+ {manifest.name} +
+ {manifest.url} + +
+
+ {manifest.images.slice(0,4).map((img) => ( +
+ {"Thumbnailthis.toggleImageModal(true, img.url)} + style={{cursor: "pointer"}} + width="40px" + /> +
+ ))} +
+
+ + this.handleOpen("editOpen", manifest)} /> + this.handleOpen("deleteOpen", manifest)} /> + +
+
+
+ ) + } + + + render() { + return ( +
+ + this.handleClose("editOpen")} + manifest={this.state.openManifest} + updateManifest={this.props.updateManifest} + manifests={this.props.manifests} + /> + this.handleClose("deleteOpen")} + deleteManifest={()=>this.props.deleteManifest({manifest: {id: this.state.openManifest.id}})} + /> +

Current Manifests

+ {Object.keys(this.props.manifests).map(this.renderManifest)} + this.toggleImageModal(false)} + contentStyle={{background: "none", boxShadow: "inherit"}} + bodyStyle={{padding:0}} + > + + +
+ ); + } +} +export default ManageManifests; \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/MapImages.js b/viscoll-app/src/components/imageManager/MapImages.js new file mode 100644 index 00000000..5ba099ea --- /dev/null +++ b/viscoll-app/src/components/imageManager/MapImages.js @@ -0,0 +1,436 @@ +import React, {Component} from 'react'; +import { DragDropContext } from 'react-dnd'; +import HTML5Backend from 'react-dnd-html5-backend'; +import SideBin from './mapImages/SideBin'; +import ImageBin from './mapImages/ImageBin'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; +import update from 'immutability-helper'; +import IconButton from 'material-ui/IconButton'; +import ArrowDown from 'material-ui/svg-icons/navigation/arrow-downward'; +import ArrowUp from 'material-ui/svg-icons/navigation/arrow-upward'; +import ImageViewer from "../global/ImageViewer"; +import Dialog from 'material-ui/Dialog'; + +class MapImages extends Component { + + constructor(props) { + super(props); + this.state = { + imageMapBoard: [], + imageMapBoardByID: {}, + sideMapBoardByID: {}, + sideMapBoard: [], + sideBacklogByID: {}, + sideBacklog: [], + activeManifest: Object.keys(props.manifests).length>0? Object.keys(props.manifests)[0]:"", + initiallyLinkedSides: [], + imageModalOpen: false, + activeImage: null + } + } + + componentWillUnmount = () => { + cancelAnimationFrame(this.requestedFrame) + } + + scheduleUpdate = (updateFn) => { + this.pendingUpdateFn = updateFn + if (!this.requestedFrame) { + this.requestedFrame = requestAnimationFrame(this.drawFrame) + } + } + + toggleImageModal = (imageModalOpen, activeImage) => { + this.setState({imageModalOpen, activeImage}) + } + + drawFrame = () => { + const nextState = update(this.state, this.pendingUpdateFn) + this.setState(nextState) + this.pendingUpdateFn = null + this.requestedFrame = null + } + + componentWillMount() { + let imageBacklogs = {}; + let sideBacklogByID = {}; + let sideBacklog = []; + let sideMapBoardByID = {}; + let sideMapBoard = []; + let imageMapBoard = []; + let imageMapBoardByID = {}; + let linkedImages = {}; + let initiallyLinkedSides = []; + + // Set up linkedImages dictionary + for (const manifestID in this.props.manifests) { + linkedImages[manifestID]=[]; + } + + const rectoIDs = Object.keys(this.props.Rectos); + const versoIDs = Object.keys(this.props.Versos); + for (const i in rectoIDs) { + const recto = this.props.Rectos[rectoIDs[i]]; + const verso = this.props.Versos[versoIDs[i]]; + const rectoDraggableItem = {id: recto.id, sideType: "Recto", leafOrder: recto.parentOrder, folioNumber: recto.folio_number}; + const versoDraggableItem = {id: verso.id, sideType: "Verso", leafOrder: verso.parentOrder, folioNumber: verso.folio_number}; + // Add sides to board or backlog depending if they're linked to images + if (recto.image.manifestID!==undefined && recto.image.manifestID.length>0) { + sideMapBoardByID[recto.id]=(rectoDraggableItem); + sideMapBoard.push(rectoDraggableItem); + const imgObj = {id: recto.image.label, manifestID: recto.image.manifestID, url: recto.image.url, binOrigin:"imageBacklog_"+this.props.manifests[recto.image.manifestID].name}; + imageMapBoard.push(imgObj); + imageMapBoardByID[recto.image.label] = imgObj; + linkedImages[recto.image.manifestID].push(recto.image.url); + initiallyLinkedSides.push({id: recto.id, url: recto.image.url}); + } else { + sideBacklog.push(rectoDraggableItem); + sideBacklogByID[recto.id]=rectoDraggableItem; + } + if (verso.image.manifestID!==undefined && verso.image.manifestID.length>0) { + sideMapBoard.push(versoDraggableItem); + sideMapBoardByID[verso.id]=(versoDraggableItem); + const imgObj = {id: verso.image.label, manifestID: verso.image.manifestID, url: verso.image.url, binOrigin:"imageBacklog_"+this.props.manifests[verso.image.manifestID].name}; + imageMapBoard.push(imgObj) + imageMapBoardByID[verso.image.label] = imgObj; + linkedImages[verso.image.manifestID].push(verso.image.url); + initiallyLinkedSides.push({id: verso.id, url: verso.image.url}); + } else { + sideBacklog.push(versoDraggableItem); + sideBacklogByID[verso.id]=versoDraggableItem; + } + } + for (const manifestID in this.props.manifests) { + const manifest = this.props.manifests[manifestID]; + // Add the initial parent bin to each image object + // const images = manifest.images.filter((image)=>{return !linkedImages[manifestID].includes(image.url)}).map((image)=>{return {id: image.label, manifestID: manifestID, url: image.url, binOrigin:"imageBacklog_"+manifest.name}}); + const images = manifest.images.filter((image)=>{return !linkedImages[manifestID].includes(image.url)}); + let imageBacklog = []; + let imageBacklogByID = {}; + for (const image of images) { + const imgObj = {id: image.label, manifestID: manifestID, url: image.url, binOrigin:"imageBacklog_"+manifest.name}; + imageBacklog.push(imgObj) + imageBacklogByID[image.label] = imgObj; + } + imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')+"ByID"] = {...imageBacklogByID}; + imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')] = imageBacklog; + } + // console.log(imageBacklogs); + this.setState({...imageBacklogs, imageMapBoard, imageMapBoardByID, sideMapBoard, sideMapBoardByID, sideBacklog, sideBacklogByID, initiallyLinkedSides}); + } + + moveItem = (id, afterId, binName) => { + // console.log("moveItem", id, afterId, binName); + if (binName.includes("Backlog")) binName = binName.substring(0,38).replace(/ /g, ''); + + const binByID = this.state[binName+"ByID"]; + const binByIndex = this.state[binName]; + + const item = binByID[id] + const afterItem = binByID[afterId] + + const itemIndex = binByIndex.indexOf(item); + const afterIndex = binByIndex.indexOf(afterItem); + + this.scheduleUpdate({ + [binName]: { + $splice: [[itemIndex, 1], [afterIndex, 0, item]], + }, + }) + } + + + changeBins = (fromBinID, toBinID, item, addToFrontOfList) => { + // console.log("changeBins", fromBinID, toBinID, item, addToFrontOfList); + if (fromBinID.includes("Backlog")) { + fromBinID = fromBinID.substring(0,38).replace(/ /g, ''); + } else if (toBinID.includes("Backlog")) { + toBinID = toBinID.substring(0,38).replace(/ /g, ''); + } + let fromBin = this.state[fromBinID]; + fromBin.splice(fromBin.indexOf(item),1); + let fromBinByID = this.state[fromBinID+"ByID"]; + delete fromBinByID[item.id]; + let toBin = this.state[toBinID]; + addToFrontOfList ? toBin.unshift(item) : toBin.push(item); + let toBinByID = this.state[toBinID+"ByID"]; + toBinByID[item.id]=item; + // updating the state inside a requestAnimationFrame callback, + if (!this.requestedFrame) { + this.requestedFrame = requestAnimationFrame(()=>{ + this.setState({[fromBinID]:fromBin, [toBinID]:toBin, [fromBinID+"ByID"]:fromBinByID, [toBinID+"ByID"]:toBinByID}) + this.pendingUpdateFn = null + this.requestedFrame = null + }) + } + } + + getIndex = (obj, parentBoardID) => { + if (parentBoardID.includes("Backlog")) parentBoardID = parentBoardID.substring(0,38).replace(/ /g, ''); + return this.state[parentBoardID].indexOf(obj); + } + + handleChange = (event, index, activeManifest) => this.setState({activeManifest}); + + addAll = (fromBoard, toBoard) => { + const newToList = this.state[toBoard].concat(this.state[fromBoard]); + this.setState({[fromBoard]:[], [toBoard]:newToList}); + } + + resetImageBacklog = () => { + let imageBacklogs = {}; + for (const manifestID in this.props.manifests) { + const manifest = this.props.manifests[manifestID]; + // Add the initial parent bin to each image object + const images = manifest.images.map((image)=>{return {id: image.label, url: image.url, binOrigin:"imageBacklog_"+manifest.name}}); + imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')] = images; + } + this.setState({imageMapBoard:[], ...imageBacklogs}); + } + submitIsDisabled = () => { + const unevenMatches = this.state.sideMapBoard.length!==this.state.imageMapBoard.length; + const noNewItems = this.state.sideMapBoard.length===this.state.initiallyLinkedSides.length && (this.state.sideMapBoard.filter((side, index)=>this.state.initiallyLinkedSides.find((initSide)=>{return initSide.id===side.id && this.state.imageMapBoard[index]!==undefined && initSide.url===this.state.imageMapBoard[index].url})!==undefined)).length===this.state.sideMapBoard.length; + const wantToUnlinkEverything = this.state.initiallyLinkedSides.length>0 && this.state.sideMapBoard.length===0 && this.state.imageMapBoard.length===0; + return !wantToUnlinkEverything && (unevenMatches || noNewItems); + } + + submitMapping = () => { + if (!this.submitIsDisabled()) { + let unlinkedSideIDs = this.state.initiallyLinkedSides.filter((initialSide)=>{ + const stillInBoard = this.state.sideMapBoard.filter((side)=>{return side.id===initialSide.id}); + return stillInBoard.length===0; + }).map((item)=>item.id); + this.setState({initiallyLinkedSides: this.state.sideMapBoard.map((obj, index)=>{return {id: obj.id, url: this.state.imageMapBoard[index].url}})}, ()=>{this.props.mapSidesToImages(this.state.sideMapBoard, this.state.imageMapBoard, unlinkedSideIDs)}); + } + } + + automatch = () => { + let sidesToMap = []; + let imagesToMap = {}; + for (const side of this.state.sideBacklog) { + let imageMatch; + // Look through manifests to find an image with an id equal to the side's folio number + for (const manifestID in this.props.manifests) { + const manifestName = this.props.manifests[manifestID].name; + imageMatch = this.state["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].find((image)=>image.id===side.folioNumber); + if (imageMatch!==undefined) { + // Found a match! Record the side and image + sidesToMap.push(side); + if (!imagesToMap.hasOwnProperty("imageBacklog_"+manifestName.substring(0,25).replace(/ /g, ''))) imagesToMap["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')]=[]; + imagesToMap["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].push(imageMatch); + break; + } + } + } + // Add items to the board + this.moveMultipleItems("sideBacklog", "sideMapBoard", sidesToMap, false); + for (const imgBinName in imagesToMap) { + this.moveMultipleItems(imgBinName, "imageMapBoard", imagesToMap[imgBinName], false); + } + } + + moveMultipleItems = (fromBinID, toBinID, items, addToFrontOfList) => { + let fromBin = this.state[fromBinID]; + let fromBinByID = this.state[fromBinID+"ByID"]; + let toBin = this.state[toBinID]; + let toBinByID = this.state[toBinID+"ByID"]; + for (const item of items) { + fromBin.splice(fromBin.indexOf(item),1); + delete fromBinByID[item.id]; + addToFrontOfList ? toBin.unshift(item) : toBin.push(item); + toBinByID[item.id]=item; + } + + // updating the state inside a requestAnimationFrame callback, + if (!this.requestedFrame) { + this.requestedFrame = requestAnimationFrame(()=>{ + this.setState({[fromBinID]:fromBin, [toBinID]:toBin, [fromBinID.substring(0,25).replace(/ /g, '')+"ByID"]:fromBinByID, [toBinID.substring(0,25).replace(/ /g, '')+"ByID"]:toBinByID}) + this.pendingUpdateFn = null + this.requestedFrame = null + }) + } + } + + automatchDisabled = () => { + for (const side of this.state.sideBacklog) { + // Look through manifests to find an image with an id equal to the side's folio number + for (const manifestID in this.props.manifests) { + const manifestName = this.props.manifests[manifestID].name; + // console.log(("imageBacklog_"+manifestName), this.state["imageBacklog_"+manifestName]); + const imageMatch = this.state["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].find((image)=>image.id===side.folioNumber); + if (imageMatch!==undefined) { + // Found a match! + return false; + } + } + } + return true; + } + + render() { + if (Object.keys(this.props.manifests).length>0) { + return ( +
+
+
+
+
+
+ this.addAll("sideMapBoard", "sideBacklog")} + > + + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
Sides backlog
+
+ this.addAll("sideBacklog", "sideMapBoard")} + > + + +
+
+
+ +
+
+
+
+
Images backlog
+
+ this.addAll("imageBacklog_"+this.props.manifests[this.state.activeManifest].name.substring(0,25).replace(/ /g, ''), "imageMapBoard")} + > + + +
+
+
+
Manifest:
+
+ + {Object.keys(this.props.manifests).map((manifestID)=> + 40? this.props.manifests[manifestID].name.slice(0,40) + "..." : this.props.manifests[manifestID].name} /> + )} + +
+
+
+ {Object.keys(this.props.manifests).map((manifestID)=> { + const manifest = this.props.manifests[manifestID]; + return + })} +
+
+
+
+
Mapping {this.state.sideMapBoard.length} sides to {this.state.imageMapBoard.length} images
+
+ + +
+
+ this.toggleImageModal(false)} + contentStyle={{background: "none", boxShadow: "inherit"}} + bodyStyle={{padding:0}} + > + + +
+ ); + } else { + return (

Getting started

To start mapping images to the collation, please upload a manifest in the "Manage Sources" tab.

); + } + } +} + +export default DragDropContext(HTML5Backend)(MapImages); diff --git a/viscoll-app/src/components/imageManager/mapImages/Constants.js b/viscoll-app/src/components/imageManager/mapImages/Constants.js new file mode 100644 index 00000000..05c2634b --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/Constants.js @@ -0,0 +1,4 @@ +export const ItemTypes = { + IMAGE: 'image', + SIDE: 'side', +} \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/mapImages/ImageBin.js b/viscoll-app/src/components/imageManager/mapImages/ImageBin.js new file mode 100644 index 00000000..33a532fd --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/ImageBin.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import { ItemTypes } from './Constants'; +import { DropTarget } from 'react-dnd'; +import ImageItem from './ImageItem'; + +const boardTarget = { + drop(props) { + return {id: props.id} + }, + hover(props, monitor, component) { + // console.log("bin hover", props, monitor.getItem(), component); + const item = monitor.getItem(); + const moveToCorrectBacklog = !props.id.includes("imageBacklog") || (props.id.includes("imageBacklog") && item.object.binOrigin===props.id); + if (moveToCorrectBacklog && props.id!==item.parentBoardID && !props.images.find((img)=>img.id===item.id)) { + const addToFrontOfList = props.id.includes("imageBacklog"); + props.changeBins(item.parentBoardID, props.id, item.object, addToFrontOfList); + monitor.getItem().parentBoardID = props.id; + } + }, +}; + +function collect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + canDrop: monitor.canDrop(), + }; +} + +class ImageBin extends Component { + render() { + const { connectDropTarget, isOver, canDrop } = this.props; + if (this.props.isVisible) { + return connectDropTarget( +
+ {this.props.images.length===0&&this.props.id.includes("Board")?
Drag items from the "images backlog" to this area
:""} + {this.props.images.map((image, index)=> +
+ +
+ )} +
+ ); + } else { + return
; + } + } +} + + +export default DropTarget(ItemTypes.IMAGE, boardTarget, collect)(ImageBin); \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/mapImages/ImageItem.js b/viscoll-app/src/components/imageManager/mapImages/ImageItem.js new file mode 100644 index 00000000..e6ffb9b1 --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/ImageItem.js @@ -0,0 +1,82 @@ +import React, { Component } from 'react'; +import { ItemTypes } from './Constants'; +import { DragSource, DropTarget } from 'react-dnd'; +import ThumbnailIcon from 'material-ui/svg-icons/editor/insert-photo'; + +const imageSource = { + beginDrag(props) { + return { + id: props.image.id, + index: props.index, + object: props.image, + parentBoardID: props.parentBoardID, + }; + }, + isDragging(props, monitor) { + return props.image.id === monitor.getItem().id; + }, + endDrag(props, monitor) { + // const item = monitor.getItem(); + const dropResult = monitor.getDropResult(); + if (dropResult && dropResult.id!==props.parentBoardID) { + // console.log("Item: dropped " + item.id +" into ", dropResult); + } + } +}; +const imageTarget = { + drop(props) { + return {index: props.index} + }, + hover(props, monitor, component) { + const draggedId = monitor.getItem().id + const item = monitor.getItem(); + if (draggedId !== props.image.id) { + // Do not move items if they don't belong to same backlogs + if (props.parentBoardID.includes("Backlog") && item.object.binOrigin!==props.parentBoardID) return; + props.moveItem(draggedId, props.image.id, props.parentBoardID) + const updatedIndex = props.getIndex(item.object, props.parentBoardID); + if (item.index!==updatedIndex) { + item.index = updatedIndex; + item.parentBoardID = props.parentBoardID; + } + } + }, +} + +function dragCollect(connect, monitor) { + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + } +} + +function dropCollect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + canDrop: monitor.canDrop(), + }; +} + +class ImageItem extends Component { + render() { + const { image, connectDragSource, connectDropTarget, isDragging } = this.props; + return connectDragSource( + connectDropTarget( +
+ {isDragging?
: +
+
this.props.toggleImageModal(true, image.url)}> + +
+
+ { image.id } +
+ {image.binOrigin.split("imageBacklog_")[1]} +
+
} +
+ )); + } +} +export default DragSource(ItemTypes.IMAGE, imageSource, dragCollect)(DropTarget(ItemTypes.IMAGE, imageTarget, dropCollect)(ImageItem)); diff --git a/viscoll-app/src/components/imageManager/mapImages/SideBin.js b/viscoll-app/src/components/imageManager/mapImages/SideBin.js new file mode 100644 index 00000000..88121aa6 --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/SideBin.js @@ -0,0 +1,58 @@ +import React, { Component } from 'react'; +import { ItemTypes } from './Constants'; +import { DropTarget } from 'react-dnd'; +import SideItem from './SideItem'; + +const boardTarget = { + drop(props) { + return {id: props.id} + }, + hover(props, monitor, component) { + // console.log("bin hover", props, monitor.getItem(), component); + const item = monitor.getItem(); + if (props.id!==item.parentBoardID && !props.sides.find((side)=>side.id===item.id)) { + const addToFrontOfList = props.id==="sideBacklog"; + props.changeBins(item.parentBoardID, props.id, item.object, addToFrontOfList); + monitor.getItem().parentBoardID = props.id; + } + } +}; + +function collect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + canDrop: monitor.canDrop(), + }; +} + +class SideBin extends Component { + render() { + const { connectDropTarget } = this.props; + + return connectDropTarget( +
+ {this.props.sides.length===0 && this.props.id.includes("Board")?
Drag items from the "sides backlog" to this area
:""} + {this.props.sides.map((side, index)=> +
+ +
+ )} +
+ ); + } +} + + +export default DropTarget(ItemTypes.SIDE, boardTarget, collect)(SideBin); \ No newline at end of file diff --git a/viscoll-app/src/components/imageManager/mapImages/SideItem.js b/viscoll-app/src/components/imageManager/mapImages/SideItem.js new file mode 100644 index 00000000..ed6a65d3 --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/SideItem.js @@ -0,0 +1,75 @@ +import React, { Component } from 'react'; +import { ItemTypes } from './Constants'; +import { DragSource, DropTarget } from 'react-dnd'; + +const sideSource = { + beginDrag(props) { + return { + id: props.side.id, + index: props.index, + object: props.side, + parentBoardID: props.parentBoardID, + }; + }, + isDragging(props, monitor) { + return props.side.id === monitor.getItem().id; + }, + endDrag(props, monitor) { + // const item = monitor.getItem(); + const dropResult = monitor.getDropResult(); + if (dropResult && dropResult.id!==props.parentBoardID) { + // console.log("Item: dropped " + item.id +" into ", dropResult); + } + } +}; +const sideTarget = { + drop(props) { + return {index: props.index} + }, + hover(props, monitor, component) { + const draggedId = monitor.getItem().id; + if (draggedId !== props.side.id) { + props.moveItem(draggedId, props.side.id, props.parentBoardID) + const updatedIndex = props.getIndex(monitor.getItem().object, props.parentBoardID); + if (monitor.getItem().index!==updatedIndex) { + monitor.getItem().index = updatedIndex; + monitor.getItem().parentBoardID = props.parentBoardID; + } + } + }, +}; + +function dragCollect(connect, monitor) { + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + } +} + +function dropCollect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + canDrop: monitor.canDrop(), + }; +} + +class SideItem extends Component { + render() { + const { side, connectDragSource, connectDropTarget, isDragging } = this.props; + const folioNumber = side.folioNumber!=="None"? " ("+side.folioNumber+")" : ""; + return connectDragSource( + connectDropTarget( +
+ {isDragging?
: +
+
+ {"Leaf " + side.leafOrder + " " + side.sideType + folioNumber} +
+
} +
+ + )); + } +} +export default DragSource(ItemTypes.SIDE, sideSource, dragCollect)(DropTarget(ItemTypes.SIDE, sideTarget, dropCollect)(SideItem)); diff --git a/viscoll-app/src/components/infoBox/GroupInfoBox.js b/viscoll-app/src/components/infoBox/GroupInfoBox.js new file mode 100644 index 00000000..3caffbe8 --- /dev/null +++ b/viscoll-app/src/components/infoBox/GroupInfoBox.js @@ -0,0 +1,532 @@ +import React from 'react'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; +import TextField from 'material-ui/TextField'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import Visibility from 'material-ui/svg-icons/action/visibility'; +import VisibilityOff from 'material-ui/svg-icons/action/visibility-off'; +import AddGroupDialog from '../infoBox/dialog/AddGroupDialog'; +import DeleteConfirmationDialog from '../infoBox/dialog/DeleteConfirmationDialog'; +import Popover, {PopoverAnimationVertical} from 'material-ui/Popover'; +import Menu from 'material-ui/Menu'; +import Chip from 'material-ui/Chip'; +import IconButton from 'material-ui/IconButton'; +import IconAdd from 'material-ui/svg-icons/content/add'; +import IconPencil from 'material-ui/svg-icons/content/create'; +import Avatar from 'material-ui/Avatar'; +import AddNote from './dialog/AddNote'; +import NoteDialog from './dialog/NoteDialog'; + +export default class GroupInfoBox extends React.Component { + constructor(props) { + super(props); + this.state = { + activeNote: null, + ...this.emptyAttributeState(), + ...this.otherAttributeStates(), + ...this.visibilityHoverState(), + addButtonPopoverOpen: false, + addGroupDialogOpen: false, + addLeafDialogOpen: false, + } + this.batchSubmit = this.batchSubmit.bind(this); + this.hasActiveAttributes = this.hasActiveAttributes.bind(this); + } + + visibilityHoverState() { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["visibility_hover_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes and if its toggled on or off during batch edit + // This is used for the checkbox states + otherAttributeStates() { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["batch_" + this.props.defaultAttributes[i]["name"]]=false; + state["editing_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes with no values + emptyAttributeState() { + let state = {}; + for (var i in this.props.defaultAttributes) { + state[this.props.defaultAttributes[i]["name"]]=""; + } + return state; + } + + componentWillReceiveProps(nextProps) { + if (this.props.selectedGroups.length < 2) { + this.setState({...this.emptyAttributeState()}); + } + if (nextProps.commonNotes.length===0) { + this.setState({activeNote:null}); + } + // Update active note + nextProps.commonNotes.forEach((noteID)=> { + if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { + this.setState({activeNote: nextProps.Notes[noteID]}); + } + }); + } + + hasActiveAttributes() { + for (var i in this.props.defaultAttributes) { + if (this.state["batch_" + this.props.defaultAttributes[i]["name"]] && + this.state[this.props.defaultAttributes[i]["name"]]!=="keep" && + this.state[this.props.defaultAttributes[i]["name"]]!=="") { + return true; + } + } + return false; + } + + toggleAddGroupDialog = (value=false) => { + this.setState({ addGroupDialogOpen: value, addButtonPopoverOpen: false, }) + } + + toggleAddLeafDialog = (value=false) => { + this.setState({ addLeafDialogOpen: value, addButtonPopoverOpen: false, }) + } + + handleAddButtonTouchTap = (event) => { + event.preventDefault(); + this.setState({ + addButtonPopoverOpen: true, + popoverAnchorEl: event.currentTarget, + }); + }; + + handleAddButtonRequestClose = () => { + this.setState({ + addButtonPopoverOpen: false, + }); + }; + + + toggleCheckbox(target, value) { + let newToggleState = {}; + newToggleState["batch_"+target]=value; + this.setState(newToggleState); + } + + dropDownChange = (event, index, value, attributeName) => { + if (Object.keys(this.props.selectedGroups).length===1) { + // In single edit - we submit change immediately + this.singleSubmit(attributeName, value); + } else { + // In batch edit - save change of attribute to the state + let updatedAttribute = {}; + updatedAttribute[attributeName] = value; + this.setState(updatedAttribute); + } + } + + onTextboxChange = (value, attributeName) => { + let newAttributeState = {}; + newAttributeState[attributeName] = value; + let newEditingState = {}; + newEditingState["editing_"+attributeName] = true; + this.setState({...newAttributeState,...newEditingState}); + }; + + textSubmit(e, attributeName) { + e.preventDefault(); + let newEditingState = {}; + newEditingState["editing_"+attributeName] = false; + this.setState({...newEditingState}); + if (!this.state.isBatch) { + this.singleSubmit(attributeName, this.state[attributeName]); + } + } + + textCancel(e, attributeName) { + let newAttributeState = {}; + newAttributeState[attributeName] = + this.props.project.Groups[this.props.selectedGroups[0]][attributeName]; + let newEditingState = {}; + newEditingState["editing_"+attributeName] = false; + this.setState({...newAttributeState,...newEditingState}); + } + + singleSubmit(attributeName, value) { + let group = {}; + group[attributeName] = value; + let id = this.props.selectedGroups[0]; + this.props.action.updateGroup(id, group); + } + + batchSubmit() { + let attributes = {}; + for (var i in this.props.defaultAttributes) { + let attrName = this.props.defaultAttributes[i]["name"]; + let attrValue = this.state[this.props.defaultAttributes[i]["name"]]; + if (attrValue !== "" && attrValue !== "keep" && attrValue !== "Keep same") { + attributes[attrName] = attrValue; + } + } + let groups = []; + for (let id of this.props.selectedGroups) { + groups.push({id, attributes}); + } + this.props.action.updateGroups(groups); + // Reset states + this.setState({...this.otherAttributeStates()}); + } + + getAttributeValues(selectedGroups=this.props.selectedGroups) { + let groupAttributes = {}; + for (var i in this.props.defaultAttributes) { + let attributeName = this.props.defaultAttributes[i]['name']; + for (let id of selectedGroups) { + let group = this.props.Groups[id]; + if (groupAttributes[attributeName]===undefined) { + groupAttributes[attributeName] = group[attributeName]; + } else if (groupAttributes[attributeName]!==group[attributeName]) { + groupAttributes[attributeName] = null; + break; + } + } + } + return groupAttributes; + } + + renderNotes = () => { + let chips = []; + for (let noteID of this.props.commonNotes) { + const note = this.props.Notes[noteID]; + let deleteFn = () => {this.props.action.unlinkNote(note.id)}; + if (this.props.isReadOnly) deleteFn = null; + chips.push( + this.setState({activeNote: note})} + > + {note.title} + ); + } + return chips; + } + + closeNoteDialog = () => { + this.setState({activeNote: null}); + } + + toggleTacketing = () => { + this.props.action.toggleTacket(this.props.selectedGroups[0]); + this.handleAddButtonRequestClose(); + } + + render() { + const isBatch = this.props.selectedGroups.length > 1; + let attributeDivs = []; + let groupAttributes = this.getAttributeValues(); + this.props.defaultAttributes.forEach((attributeDict)=> { + // Generate checkbox if we're in batch edit mode + let label = attributeDict.displayName; + // Generate eye toggle checkbox + let eyeCheckbox = ""; + + let eyeStyle = {}; + let eyeIsChecked = this.props.visibleAttributes[attributeDict.name]; + if (this.props.viewMode!=="TABULAR") { + if (attributeDict.name==="type") { + eyeStyle = {fill: "#C2C2C2", cursor:"not-allowed"}; + eyeIsChecked = true; + } + } + if (isBatch && !this.props.isReadOnly) { + eyeCheckbox = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px",...eyeStyle}} + iconStyle={{marginRight:"10px",...eyeStyle}} + checked={eyeIsChecked} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
0?{display:"none"}:{}}> + {eyeIsChecked? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ label = this.toggleCheckbox(attributeDict.name,value)} + labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} + checked={this.state["batch_"+attributeDict.name]} + style={{display:"inline-block",width:"25px"}} + iconStyle={{marginRight:"10px"}} + />; + } else { + // In single edit - display eye icon with label + label = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px",...eyeStyle}} + iconStyle={{marginRight:"10px", color:"gray",...eyeStyle}} + checked={eyeIsChecked} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
0?{display:"none"}:{}}> + {eyeIsChecked? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ } + // Generate dropdown or text box depending on the current attribute + let input = groupAttributes[attributeDict.name]; + if (!this.props.isReadOnly) { + if (attributeDict.options!==undefined) { + // Drop down menu + let menuItems = []; + attributeDict.options.forEach((option, index)=> { + menuItems.push(); + }); + if (groupAttributes[attributeDict.name]===null) { + menuItems.push(); + } + let value = "keep"; + if (this.state[attributeDict.name]!=="" && isBatch) { + value = this.state[attributeDict.name]; + } else if (groupAttributes[attributeDict.name]!==null) { + value = groupAttributes[attributeDict.name]; + } + input = (this.dropDownChange(e,i,v,attributeDict.name)} + fullWidth={true} + disabled={isBatch && !this.state["batch_"+attributeDict.name]} + > + {menuItems} + + ); + } else { + // Text box + let textboxButtons = ""; + if (!isBatch && this.state["editing_"+attributeDict.name]) { + textboxButtons = ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.textSubmit(e,attributeDict.name)} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.textCancel(e,attributeDict.name)} + /> +
+ ); + } + let value = "Keep same"; + if (this.state["editing_"+attributeDict.name]) { + value = this.state[attributeDict.name]; + } else if (groupAttributes[attributeDict.name]) { + value = groupAttributes[attributeDict.name]; + } + input = (
+
this.textSubmit(e,attributeDict.name)}> + this.onTextboxChange(v,attributeDict.name)} + disabled={isBatch && !this.state["batch_"+attributeDict.name]} + /> + {textboxButtons} + +
) + } + } else { + if (!input && this.props.selectedGroups.length>1) { + input =
Different values
; + } else { + input =
{input}
; + } + } + attributeDivs.push( +
+
+ {eyeCheckbox} + {label} +
+
+ {input} +
+
+ ); + }); + let submitBtn = ""; + if (isBatch && this.hasActiveAttributes()) { + submitBtn = + } + let addBtn = ""; + let addButtonPopover = ""; + if (!isBatch) { + addButtonPopover = + + this.toggleAddGroupDialog(true)} /> + this.toggleAddLeafDialog(true)}/> + + + + addBtn = + } + let deleteBtn = + + let attributeTacket = +
+
+ + + +
+

Tacket

+
+ if (!this.state.isBatch && this.props.Groups[this.props.selectedGroups[0]].tacketed!=="") { + attributeTacket = +
+
+

Tacket

+ {this.singleSubmit("tacketed", "")}:null} + > +
+
+ {"Tacketed to Leaf " + this.props.Leafs[this.props.Groups[this.props.selectedGroups[0]].tacketed].order } +
+
+
+ }/> +
+
+
+
+
+
+
+ } + const notes = this.renderNotes(); + return ( +
+
+ {attributeDivs} + {!this.props.isReadOnly? + : ""} +

{this.props.selectedGroups.length>1?"Notes in common" : "Notes"}

+
+ {notes} +
+
+ {attributeTacket} + {submitBtn} + {addButtonPopover} +
+
+

Actions

+ {addBtn} + {deleteBtn} +
+ + + +
+ ); + } +} diff --git a/viscoll-app/src/components/infoBox/LeafInfoBox.js b/viscoll-app/src/components/infoBox/LeafInfoBox.js new file mode 100644 index 00000000..86fc3ef0 --- /dev/null +++ b/viscoll-app/src/components/infoBox/LeafInfoBox.js @@ -0,0 +1,504 @@ +import React from 'react'; +import AddLeafDialog from '../infoBox/dialog/AddLeafDialog'; +import DeleteConfirmationDialog from '../infoBox/dialog/DeleteConfirmationDialog'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; +import Visibility from 'material-ui/svg-icons/action/visibility'; +import VisibilityOff from 'material-ui/svg-icons/action/visibility-off'; +import {getLeafsOfGroup} from '../../helpers/getLeafsOfGroup'; +import Chip from 'material-ui/Chip'; +import AddNote from './dialog/AddNote'; +import NoteDialog from './dialog/NoteDialog'; +import ImageViewer from "../global/ImageViewer"; +import Dialog from 'material-ui/Dialog'; + +export default class LeafInfoBox extends React.Component { + + constructor(props) { + super(props); + + this.state = { + imageModalOpen: false, + activeNote: null, + isBatch: this.props.selectedLeaves.length>1, + ...this.emptyAttributeState(), + ...this.batchAttributeToggleState(), + ...this.visibilityHoverState(), + } + } + + + visibilityHoverState() { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["visibility_hover_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes and if its toggled on or off during batch edit + // This is used for the checkbox states + batchAttributeToggleState = () => { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["batch_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes with no values + emptyAttributeState = () => { + let state = {}; + for (var i in this.props.defaultAttributes) { + if (this.props.defaultAttributes[i]["name"]==="attached_to") { + state[this.props.defaultAttributes[i]["name"]]=[]; + } else { + state[this.props.defaultAttributes[i]["name"]]=""; + } + } + return state; + } + + componentWillReceiveProps(nextProps) { + this.setState({ + isBatch: nextProps.selectedLeaves.length>1, + }); + if (!this.state.isBatch) { + this.setState({...this.emptyAttributeState()}); + } + if (nextProps.commonNotes.length===0) { + this.setState({activeNote:null}); + } + // Update active note + nextProps.commonNotes.forEach((noteID)=> { + if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { + this.setState({activeNote: nextProps.Notes[noteID]}); + } + }); + } + + hasActiveAttributes = () => { + for (var i in this.props.defaultAttributes) { + if (this.state["batch_" + this.props.defaultAttributes[i]["name"]] && + this.state[this.props.defaultAttributes[i]["name"]]!=="keep" && + this.state[this.props.defaultAttributes[i]["name"]]!=="") { + return true; + } + } + return false; + } + + dropDownChange = (event, index, value, attributeName) => { + if (this.props.selectedLeaves.length===1) { + // In single edit - we submit change immediately + let attributes = {}; + attributes[attributeName] = value; + let leaf = { + ...attributes, + }; + this.props.action.updateLeaf(this.props.selectedLeaves[0], leaf); + } else { + // In batch edit - save change of attribute to the state + let updatedAttribute = {}; + updatedAttribute[attributeName] = value; + this.setState(updatedAttribute); + } + } + + onConjoinChange = (event, index, leaf, newID) => { + let request = {conjoined_to: newID }; + this.props.action.updateLeaf(leaf.id, request); + } + + onAttachedToChange = (event, activeLeaf, location, id, method) => { + let request = {attached_to: activeLeaf.attached_to}; + if (method==="None") { + request.attached_to[location+"ID"]=""; + request.attached_to[location+"Method"]=""; + } else { + request.attached_to[location+"ID"]=id; + request.attached_to[location+"Method"]=method; + } + this.props.action.updateLeaf(activeLeaf.id, request); + } + + batchSubmit = () => { + let attributes = {}; + for (var i in this.props.defaultAttributes) { + let attrName = this.props.defaultAttributes[i]["name"]; + let attrValue = this.state[this.props.defaultAttributes[i]["name"]]; + if (attrValue !== "" && attrValue !== "keep") { + attributes[attrName] = attrValue; + } + } + let leafs = []; + for (var key of this.props.selectedLeaves) { + const leaf = this.props.Leafs[key]; + leafs.push({id: leaf.id, attributes}); + } + this.props.action.updateLeafs(leafs); + // Reset states + this.setState({...this.batchAttributeToggleState()}); + } + + // Returns dictionary of attribute names and values + // If multiple selected leaves have conflicting values, + // the value of that attribute will be set to null + getAttributeValues() { + let leafAttributes = {}; + for (var i in this.props.defaultAttributes) { + let attributeName = this.props.defaultAttributes[i]['name']; + for (var key of this.props.selectedLeaves) { + const leaf = this.props.Leafs[key]; + if (leafAttributes[attributeName]===undefined) { + leafAttributes[attributeName] = leaf[attributeName]; + } else if (leafAttributes[attributeName]!==leaf[attributeName]) { + leafAttributes[attributeName] = null; + break; + } + } + } + return leafAttributes; + } + + // Handle checkbox toggling by updating relevant attribute state + toggleCheckbox = (target, value) => { + let newToggleState = {}; + newToggleState["batch_"+target]=value; + this.setState(newToggleState); + } + + renderNotes = () => { + let chips = []; + for (let noteID of this.props.commonNotes) { + const note = this.props.Notes[noteID]; + let deleteFn = () => {this.props.action.unlinkNote(note.id)}; + if (this.props.isReadOnly) deleteFn = null; + chips.push( + this.setState({activeNote: note})} + > + {note.title} + ); + } + return chips; + } + + closeNoteDialog = () => { + this.setState({activeNote:null}); + } + + toggleImageModal = (imageModalOpen) => { + this.setState({imageModalOpen}) + } + + render() { + let leafAttributes = this.getAttributeValues(); + let attributeDivs = []; + const activeLeaf = this.props.Leafs[this.props.selectedLeaves[0]]; + const parentGroup = this.props.Groups[activeLeaf.parentID]; + const leafMembersOfCurrentGroup = getLeafsOfGroup(parentGroup, this.props.Leafs); + const isFirstLeaf = activeLeaf.memberOrder===1; + const isLastLeaf = activeLeaf.id===leafMembersOfCurrentGroup[leafMembersOfCurrentGroup.length-1].id; + const hasOnlyActiveLeaf = leafMembersOfCurrentGroup.length===2; // 2 because there's none leaf + + // Generate drop down for each leaf attribute + this.props.defaultAttributes.forEach((attributeDict)=> { + if (attributeDict.name.includes("attached_to")) { + if (hasOnlyActiveLeaf || (isFirstLeaf && attributeDict.name.includes("above")) || (isLastLeaf && attributeDict.name.includes("below"))) { + return; + } + } + let label =
{attributeDict.displayName}
; + // Generate eye toggle checkbox + let eyeCheckbox = ""; + if (this.props.viewMode==="TABULAR" && this.state.isBatch) { + + eyeCheckbox = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px"}} + iconStyle={{marginRight:"10px"}} + checked={this.props.visibleAttributes[attributeDict.name]} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
+ {this.props.visibleAttributes[attributeDict.name]? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ } else if (this.props.viewMode==="TABULAR") { + // In single edit tabular mode - display eye icon with label + label = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px"}} + checked={this.props.visibleAttributes[attributeDict.name]} + iconStyle={{marginRight:"10px", color:"gray"}} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
+ {this.props.visibleAttributes[attributeDict.name]? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ } + if (this.state.isBatch && !this.props.isReadOnly) { + // In batch edit for either edit modes + label = this.toggleCheckbox(attributeDict.name,value)} + labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} + checked={this.state["batch_"+attributeDict.name]} + style={{display:"inline-block",width:"25px"}} + iconStyle={{marginRight:"10px"}} + disabled={(attributeDict.name==="conjoined_leaf_order"||attributeDict.name.includes("attached_to"))} + />; + } + let input = leafAttributes[attributeDict.name]; + if (!this.props.isReadOnly) { + if (attributeDict.name==="conjoined_leaf_order") { + let menuItems = []; + leafMembersOfCurrentGroup.forEach((member)=> { + menuItems.push( + ); + }); + input = + this.onConjoinChange(e,i,activeLeaf,v)} + fullWidth={true} + disabled={this.state.isBatch} + > + {menuItems} + + } else { + // Populate drop down items + let menuItems = []; + attributeDict.options.forEach((option, index)=> { + menuItems.push(); + }); + if (leafAttributes[attributeDict.name]===null) { + menuItems.push(); + } + let value = "keep"; + if (this.state[attributeDict.name]!=="" && this.state.isBatch) { + value = this.state[attributeDict.name]; + } else if (leafAttributes[attributeDict.name]!==null) { + value = leafAttributes[attributeDict.name]; + } + input = + this.dropDownChange(e,i,v,attributeDict.name)} + fullWidth={true} + disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + > + {menuItems} + + } + } else if (!input && this.props.selectedLeaves.length>1) { + // We're in readOnly mode with no common attribute value + input =
Different values
; + } + attributeDivs.push( +
+
+ {eyeCheckbox} + {label} +
+
+ {input} +
+
+ ); + }); + let submitBtn = ""; + if (this.state.isBatch && this.hasActiveAttributes()) { + submitBtn = + } + let addBtn = ""; + if (!this.state.isBatch) { + addBtn = + } + let deleteBtn = ( + + ); + + let conjoinButton = ( + + ); + if (this.props.selectedLeaves.length<2){ + conjoinButton = ""; + } else { + let parentIDs = this.props.selectedLeaves.map((leafID)=>{return this.props.Leafs[leafID].parentID}) + let parentIDsSet = new Set(parentIDs); + if (parentIDsSet.size!==1) + conjoinButton = ""; + } + + let imageModalContent; + let imageThumbnails = []; + if (this.props.viewMode!=="VIEWING") { + // Show the side image if available + if (this.props.selectedLeaves.length===1) { + const leaf = this.props.Leafs[this.props.selectedLeaves[0]] + const recto = this.props.Rectos[leaf.rectoID]; + const verso = this.props.Versos[leaf.versoID]; + // replace imageModalContent view OSD component + const rectoURL = recto.image ? recto.image.url : null; + const versoURL = verso.image ? verso.image.url : null; + imageModalContent = (); + if (rectoURL) { + imageThumbnails.push( +
+ {recto.folio_number}this.toggleImageModal(true)} + style={{cursor: "pointer"}} + /> +
+ {recto.folio_number} +
+ ) + } + if (versoURL) { + imageThumbnails.push( +
+ {verso.folio_number}this.toggleImageModal(true)} + style={{cursor: "pointer"}} + /> +
+ {verso.folio_number} +
+ ) + } + } + } + + const notes = this.renderNotes(); + return ( +
+ {attributeDivs} +
+ {imageThumbnails} +
+ {this.props.isReadOnly&¬es.length===0?"": +
+ {this.props.isReadOnly?"": + } +
+

+ {Object.keys(this.props.selectedLeaves).length>1?"Notes in common" : "Notes"} +

+
+ {notes} +
+
+
+ } + {submitBtn} + + {this.props.isReadOnly?"": +
+

Actions

+ {conjoinButton} + {addBtn} + {deleteBtn} +
+ } + + + this.toggleImageModal(false)} + contentStyle={{background: "none", boxShadow: "inherit"}} + bodyStyle={{padding:0}} + > + {imageModalContent} + +
+ ); + } +} diff --git a/viscoll-app/src/components/infoBox/SideInfoBox.js b/viscoll-app/src/components/infoBox/SideInfoBox.js new file mode 100644 index 00000000..d9feeb14 --- /dev/null +++ b/viscoll-app/src/components/infoBox/SideInfoBox.js @@ -0,0 +1,475 @@ +import React from 'react'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; +import TextField from 'material-ui/TextField'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import Visibility from 'material-ui/svg-icons/action/visibility'; +import VisibilityOff from 'material-ui/svg-icons/action/visibility-off'; +import Chip from 'material-ui/Chip'; +import AddNote from './dialog/AddNote'; +import NoteDialog from './dialog/NoteDialog'; +import Dialog from 'material-ui/Dialog'; +import ImageViewer from "../global/ImageViewer"; + + +export default class SideInfoBox extends React.Component { + constructor(props) { + super(props); + this.state = { + activeNote: null, + imageModalOpen: false, + isBatch: this.props.selectedSides.length>1, + ...this.emptyAttributeState(), + ...this.otherAttributeStates(), + ...this.visibilityHoverState(), + } + } + + visibilityHoverState = () => { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["visibility_hover_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes and if its toggled on or off during batch edit + // This is used for the checkbox states + otherAttributeStates = () => { + let state = {}; + for (var i in this.props.defaultAttributes) { + state["batch_" + this.props.defaultAttributes[i]["name"]]=false; + state["editing_" + this.props.defaultAttributes[i]["name"]]=false; + } + return state; + } + + // Creates a dictionary of attributes with no values + emptyAttributeState = () => { + let state = {}; + for (var i in this.props.defaultAttributes) { + state[this.props.defaultAttributes[i]["name"]]=""; + } + return state; + } + + hasActiveAttributes = () => { + for (var i in this.props.defaultAttributes) { + if (this.state["batch_" + this.props.defaultAttributes[i]["name"]] && + this.state[this.props.defaultAttributes[i]["name"]]!=="keep" && + this.state[this.props.defaultAttributes[i]["name"]]!=="") { + return true; + } + } + return false; + } + + componentWillReceiveProps(nextProps) { + this.setState({ + isBatch: nextProps.selectedSides.length>1, + }); + if (!this.state.isBatch) { + this.setState({...this.emptyAttributeState()}); + } + if (nextProps.commonNotes.length===0) { + this.setState({activeNote:null}); + } + // Update active note + nextProps.commonNotes.forEach((noteID)=> { + if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { + this.setState({activeNote: nextProps.Notes[noteID]}); + } + }); + } + + + dropDownChange = (event, index, value, attributeName) => { + if (!this.state.isBatch) { + // In single edit - we submit change immediately + this.singleSubmit(attributeName, value); + } else { + // In batch edit - save change of attribute to the state + let updatedAttribute = {}; + updatedAttribute[attributeName] = value; + this.setState(updatedAttribute); + } + } + + onTextboxChange = (value, attributeName) => { + let newAttributeState = {}; + newAttributeState[attributeName] = value; + let newEditingState = {}; + newEditingState["editing_"+attributeName] = true; + this.setState({...newAttributeState,...newEditingState}); + }; + + textSubmit = (e, attributeName) => { + e.preventDefault(); + let newEditingState = {}; + newEditingState["editing_"+attributeName] = false; + this.setState({...newEditingState}); + if (!this.state.isBatch) { + this.singleSubmit(attributeName, this.state[attributeName]); + } + } + + singleSubmit = (attributeName, value) => { + let attributes = {}; + attributes[attributeName] = value; + let sideID = this.props.selectedSides[0]; + let side = + { + ...attributes + } + ; + this.props.action.updateSide(sideID, side); + } + + textCancel = (e, attributeName) => { + let newAttributeState = {}; + newAttributeState[attributeName] = + this.props.Sides[this.props.selectedSides[0]][attributeName]; + let newEditingState = {}; + newEditingState["editing_"+attributeName] = false; + this.setState({...newAttributeState,...newEditingState}); + } + + // Handle checkbox toggling by updating relevant attribute state + toggleCheckbox = (target, value) => { + let newToggleState = {}; + newToggleState["batch_"+target]=value; + this.setState(newToggleState); + } + + batchSubmit = () => { + let attributes = {}; + let sides = []; + for (var i in this.props.defaultAttributes) { + // Go through each default attributes + let attrName = this.props.defaultAttributes[i]["name"]; + if (this.state["batch_"+attrName]) { + // This side attribute was selected for batch edit + // Get its new value + let attrValue = this.state[this.props.defaultAttributes[i]["name"]]; + if (attrValue !== null && attrValue !== "keep" && attrValue !== "Keep same") { + attributes[attrName] = attrValue; + } + } + } + for (let id of this.props.selectedSides){ + if (Object.keys(attributes).length>0) { + sides.push({id, attributes}); + } + }; + this.setState({...this.otherAttributeStates()}) + this.props.action.updateSides(sides); + } + + getAttributeValues = (selectedSides=this.props.selectedSides) => { + let sideAttributes = {}; + for (var i in this.props.defaultAttributes) { + let attributeName = this.props.defaultAttributes[i]['name']; + for (let sideID of selectedSides) { + let side = this.props.Sides[sideID]; + if (sideAttributes[attributeName]===undefined) { + sideAttributes[attributeName] = side[attributeName]; + } else if (sideAttributes[attributeName]!==side[attributeName]) { + sideAttributes[attributeName] = null; + break; + } + } + } + return sideAttributes; + } + + renderNotes = () => { + let chips = []; + for (let noteID of this.props.commonNotes) { + const note = this.props.Notes[noteID]; + let deleteFn = () => {this.props.action.unlinkNote(note.id)}; + if (this.props.isReadOnly) deleteFn = null; + chips.push( + {this.setState({activeNote: note})}} + > + {note.title} + ); + } + return chips; + } + + closeNoteDialog = () => { + this.setState({activeNote:null}); + } + + toggleImageModal = (imageModalOpen) => { + this.setState({imageModalOpen}) + } + + + render() { + let attributeDivs = []; + let sideAttributes = this.getAttributeValues(); + for (var i in this.props.defaultAttributes) { + let attributeDict = this.props.defaultAttributes[i]; + if (attributeDict.name === "uri") continue; + // Generate checkbox if we're in batch edit mode + let label = attributeDict.displayName; + // Generate eye toggle checkbox + let eyeCheckbox = ""; + + let eyeStyle = {}; + let eyeIsChecked = this.props.visibleAttributes[attributeDict.name]; + if (this.props.viewMode!=="TABULAR") { + if (attributeDict.name==="uri"||attributeDict.name==="script_direction") { + eyeStyle = {fill: "#C2C2C2", cursor:"not-allowed"}; + eyeIsChecked = false; + } + } + if (this.state.isBatch && !this.props.isReadOnly) { + eyeCheckbox = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px",...eyeStyle}} + iconStyle={{marginRight:"10px",...eyeStyle}} + checked={eyeIsChecked} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
0?{display:"none"}:{}}> + {eyeIsChecked? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ label = this.toggleCheckbox(attributeDict.name,value)} + labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} + checked={this.state["batch_"+attributeDict.name]} + style={{display:"inline-block",width:"25px"}} + iconStyle={{marginRight:"10px"}} + disabled={this.state.isBatch && (attributeDict.name==="folio_number"||attributeDict.name==="uri")} + />; + } else { + // In single edit, display eye icon with label (no checkbox) + label = +
+ } + uncheckedIcon={} + onCheck={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + style={{display:"inline-block",width:"25px",...eyeStyle}} + iconStyle={{marginRight:"10px", color:"gray",...eyeStyle}} + checked={eyeIsChecked} + onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} + onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + /> +
0?{display:"none"}:{}}> + {eyeIsChecked? + "Hide attribute in the collation" + : "Show attribute in the collation" + } +
+
+ } + // Generate dropdown or text box depending on the current attribute + let input = sideAttributes[attributeDict.name]; + if (!this.props.isReadOnly) { + if (attributeDict.options!==undefined) { + // Drop down menu + let menuItems = []; + for (var j in attributeDict.options) { + let option = attributeDict.options[j]; + menuItems.push(); + } + if (sideAttributes[attributeDict.name]===null) { + menuItems.push(); + } + let value = "keep"; + if (this.state[attributeDict.name]!=="" && this.state.isBatch) { + value = this.state[attributeDict.name]; + } else if (sideAttributes[attributeDict.name]!==null) { + value = sideAttributes[attributeDict.name]; + } + input = (this.dropDownChange(e,i,v,attributeDict.name)} + fullWidth={true} + disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + > + {menuItems} + + ); + } else { + // Text box + let textboxButtons = ""; + if (!this.state.isBatch && this.state["editing_"+attributeDict.name]) { + textboxButtons = ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.textSubmit(e,attributeDict.name)} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.textCancel(e,attributeDict.name)} + /> +
+ ); + } + let value = "Keep same"; + if (this.state["editing_"+attributeDict.name]) { + value = this.state[attributeDict.name]; + } else if (sideAttributes[attributeDict.name]) { + value = sideAttributes[attributeDict.name]; + } + input = (
+
this.textSubmit(e,attributeDict.name)}> + this.onTextboxChange(v,attributeDict.name)} + disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + /> + {textboxButtons} + +
) + } + } else { + // We're in readOnly mode with no common attribute value + if (!input && this.props.selectedSides.length>1) { + input =
Different values
; + } else { + input =
{input}
+ } + } + + attributeDivs.push( +
+
+ {eyeCheckbox} + {label} +
+
+ {input} +
+
+ ); + } + const notes = this.renderNotes(); + let notesDiv = []; + if (!(this.props.isReadOnly && notes.length===0)) { + notesDiv.push( +
+ {this.props.isReadOnly?"": + this.props.action.linkNote(noteID, this.props.sideIndex), + createAndAttachNote: this.props.action.createAndAttachNote + }} + noteTypes={this.props.noteTypes} + />} +

{Object.keys(this.props.selectedSides).length>1?"Notes in common" : "Notes"}

+
+ {notes} +
+
+ ); + } + + let submitBtn = ""; + if (this.state.isBatch && this.hasActiveAttributes()) { + submitBtn = + } + + let imageModalContent; + let imageThumbnail = []; + if (this.props.viewMode!=="VIEWING") { + // Show the side image if available + if (this.props.selectedSides.length===1){ + const side = this.props.Sides[this.props.selectedSides[0]]; + // replace imageModalContent view OSD component + const rectoURL = side.memberType==="Recto" ? side.image.url : null; + const versoURL = side.memberType==="Verso" ? side.image.url : null; + imageModalContent = (); + if (side.image.url){ + imageThumbnail.push( +
+ {side.folio_number}this.toggleImageModal(true)} + style={{cursor: "pointer"}} + /> +
+ ) + } + } + } + + return ( +
+ {attributeDivs} +
+ {imageThumbnail} +
+ {notesDiv} + {submitBtn} + + this.toggleImageModal(false)} + contentStyle={{background: "none", boxShadow: "inherit"}} + bodyStyle={{padding:0}} + > + {imageModalContent} + +
+ ); + } +} diff --git a/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.js b/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.js new file mode 100644 index 00000000..e26fa5b2 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.js @@ -0,0 +1,510 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; +import TextField from 'material-ui/TextField'; +import IconButton from 'material-ui/IconButton'; +import AddCircle from 'material-ui/svg-icons/content/add-circle'; +import RemoveCircle from 'material-ui/svg-icons/content/remove-circle-outline'; +import light from '../../../styles/light'; + +/** Dialog to add groups in a collation. This component is used in the visual and tabular edit modes. It is mounted by `InfoBox` and `GroupInfoBox` components. */ +export default class AddGroupDialog extends React.Component { + constructor(props) { + super(props); + this.state = { + numberOfGroups: 1, + hasLeaves: props.addLeafs || false, + numberOfLeaves: 1, + conjoin: false, + oddLeaf: 2, + copies: 1, + location: "inside", + errorText: { + numberOfGroups: "", + numberOfLeaves: "", + oddLeaf: "", + copies: "", + }, + } + }; + + /** + * Increment a state's value by one, bounded by `max` and `min`. If the user previously + * entered an invalid value, the value is set to `min`. + * + * @param {string} name state name + * @param {number} min + * @param {number} max + * @public + */ + incrementNumber = (name, min, max) => { + let newCount = 0; + if (!this.isNormalInteger(this.state[name])) { + newCount = min; + } else { + newCount = Math.min(max, this.state[name]+1); + } + let newState = {errorText:{}}; + newState[name]=(isNaN(newCount))?min:newCount; + newState.errorText[name]=""; + this.setState({...newState}); + } + + /** + * Decrement a state by one, bounded by `max` and `min`. If the user previously + * entered an invalid value, the value is set to min. + * + * @param {string} name state name + * @param {number} min + * @param {number} max + * @public + */ + decrementNumber = (name, min, max) => { + let newCount = Math.min(max, Math.max(min, this.state[name]-1)); + let newState = {errorText:{}}; + newState[name]=(isNaN(newCount))?min:newCount; + newState.errorText[name]=""; + this.setState({...newState}); + } + + /** + * Validate user input. If invalid, display error message, otherwise update relevant state + * + * @param {string} name state name + * @param {number} value new input value + * @public + */ + onNumberChange = (name, value) => { + let errorState = this.state.errorText; + errorState[name] = ""; + if (!this.isNormalInteger(value)) { + errorState[name] = "Must be a number"; + } else { + value = parseInt(value, 10); + } + if ((name==="numberOfGroups"||name==="copies") && (value<1 || value>99)) { + errorState[name] = "Number must be between 1 and 99"; + } else if (name==="numberOfLeaves" && (value<1 || value>999)) { + errorState[name] = "Number must be between 1 and 999"; + } else if (name==="oddLeaf" && (value<1 || value>this.state.numberOfLeaves)) { + errorState[name] = "Number must be between 1 and " + this.state.numberOfLeaves; + } + let newState = {}; + newState[name] = value; + this.setState({...newState, errorText: errorState}); + } + + /** + * Check if string is an integer + * + * @param {string} str + * @public + */ + isNormalInteger = (str) => { + return /^([1-9]\d*)$/.test(str); + } + + /** + * Toggle a checkbox + * + * @param {string} stateName + * @param {boolean} value + * @public + */ + onToggleCheckbox = (stateName, value) => { + let newState = {}; + newState[stateName] = value; + this.setState(newState); + } + + /** + * Update location radio button group + * + * @param {string} value + * @public + */ + onLocationChange = (value) => { + this.setState({location: value});; + } + /** + * Returns next sibling of a group + * + * @param {number} groupOrder global order of group of interest + * @public + */ + getNextSibling = () => { + let activeGroup = this.props.Groups[this.props.selectedGroups[0]]; + for (let groupID of Object.keys(this.props.Groups).slice(activeGroup.order)) { + const group = this.props.Groups[groupID] + if (group.nestLevel===activeGroup.nestLevel) + return group + } + return null; + } + + /** + * Returns the last child group + * + * @param {array} members array of members + * @public + */ + findLastChildGroup = (members) => { + let lastGroup = null; + for (let member of members) { + if (member.memberType==="Group") { + member = this.props.Groups[member.id] + if (lastGroup===null || (member.order>lastGroup.order)) { + lastGroup = member; + } + if (member.members.length>0) { + let result = this.findLastChildGroup(member.members); + if (result && result.order>lastGroup.order) lastGroup = result; + } + } + } + return lastGroup; + } + + /** + * Submit add group request + * @public + */ + submit = () => { + if (this.props.addLeafs || !this.isDisabled()) { + let data = {group:{}, additional:{}}; + data.additional["noOfGroups"] = this.state.numberOfGroups; + if (this.state.hasLeaves) { + data.additional["noOfLeafs"] = this.state.numberOfLeaves; + data.additional["conjoin"] = (this.state.numberOfLeaves>1)? this.state.conjoin : false; + if (this.state.numberOfLeaves>1 && this.state.conjoin && !(this.state.numberOfLeaves%2===0)) { + data.additional["oddMemberLeftOut"] = this.state.oddLeaf; + } + } + if (this.props.selectedGroups.length===0){ + // Empty project. Add new group + data.additional["order"] = 1; + data.additional["memberOrder"] = 1; + data.group["type"] = "Quire"; + data.group["title"] = "None"; + } else if(this.props.addLeafs) { + // Add Leafs inside + data = {leaf:{}, additional:{...data.additional}}; + delete data.additional.noOfGroups + data.leaf["project_id"] = this.props.projectID; + data.leaf["parentID"] = this.props.selectedGroups[0]; + data.additional["memberOrder"] = 1; + this.props.action.addLeafs(data); + this.props.closeDialog(); + return; + } + else { + // Add group(s) + const group = this.props.Groups[this.props.selectedGroups[0]]; + let memberOrder = group.memberOrder; + let groupOrder = group.order; + if (group.parentID) { + // If active group is nested, the new group(s) must have the same parent as the active group + data.additional["parentGroupID"] = group.parentID; + } + if (this.state.location==="below") { + // Add group below + memberOrder += 1; + let sibling = this.getNextSibling(); + if (sibling) { + groupOrder = sibling.order; + } else { + // No sibling.. + if (!group.parentID) { + // Active group is a root group with no next sibling + groupOrder = Object.keys(this.props.Groups).length+1; + } else { + if (group.members.length>0) { + // Find the last child (possibly multi-nested) + let lastChild = this.findLastChildGroup(group.members); + if (lastChild===null) { + groupOrder = Object.keys(this.props.Groups).length+1; + } else { + groupOrder = lastChild.order + 1; + } + } else { + // If no children + groupOrder = groupOrder+1; + } + } + } + } else if (this.state.location==="inside") { + // Add group inside + groupOrder += 1; + memberOrder = 1; + data.additional["parentGroupID"] = group.id; + } + data.group = { + title: "None", + type: "Quire" + }; + data.additional["memberOrder"] = memberOrder; + data.additional["order"] = groupOrder; + } + data.group["project_id"] = this.props.projectID + this.props.action.addGroups(data); + this.props.closeDialog(); + this.setState({location: "below"}); + this.resetForm(); + } + } + + /** + * Return `true` if there are any errors in the input fields + * @public + */ + isDisabled = () => { + let copiesError = !(this.state.errorText.copies===undefined) && this.state.errorText.copies.length>0; + let numberOfGroupsError = !(this.state.errorText.numberOfGroups===undefined) && this.state.errorText.numberOfGroups.length>0; + let numberOfLeavesError = !(this.state.errorText.numberOfLeaves===undefined) && this.state.errorText.numberOfLeaves.length>0; + let oddLeafError = !(this.state.errorText.oddLeaf===undefined) && this.state.errorText.oddLeaf.length>0; + return this.state.location==="" || copiesError || numberOfGroupsError || numberOfLeavesError || oddLeafError; + } + + /** + * Reset state + * @public + */ + resetForm = () => { + this.setState({ + numberOfGroups: 1, + hasLeaves: this.props.addLeafs || false, + numberOfLeaves: 1, + conjoin: false, + oddLeaf: 2, + copies: 1, + location: "", + errorText: { + numberOfGroups: "", + numberOfLeaves: "", + oddLeaf: "", + copies: "", + }, + }); + } + + render() { + const actions = [ + {this.resetForm();this.props.closeDialog()}} + style={{width:"49%", marginRight:"1%",border:"1px solid #ddd"}} + />, + , + ]; + + const styles = { + radioButton: { + marginBottom: 5, + }, + }; + + let conjoinOption = ""; + let oddLeaf = ""; + // let copies = ""; + let numberOfLeaves = ""; + if (this.state.hasLeaves) { + numberOfLeaves = +
+
+

Number of leaves

+
+
+ this.onNumberChange("numberOfLeaves", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} + /> + this.decrementNumber("numberOfLeaves", 1, 999)} + > + + + this.incrementNumber("numberOfLeaves", 1, 999)} + > + + +
+
; + } + + if (this.state.hasLeaves && this.state.numberOfLeaves>1) { + conjoinOption = +
+
+

Conjoin leaves?

+
+
+ this.onToggleCheckbox("conjoin", v)} + /> +
+
+ } + if (this.state.hasLeaves && this.state.conjoin && !(this.state.numberOfLeaves%2===0)) { + oddLeaf = +
+
+

Odd leaf to not conjoin

+
+
+ this.onNumberChange("oddLeaf", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} + /> + this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + > + + + this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + > + + +
+
+ } + + let numberOfGroups = this.state.location?
+
+

Number of groups

+
+
+ this.onNumberChange("numberOfGroups", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} + /> + this.decrementNumber("numberOfGroups", 1, 999)} + > + + + this.incrementNumber("numberOfGroups", 1, 999)} + > + + +
+
: ""; + + let radioButtonGroupHeader =

Add new group(s)

; + let radioButtonGroup = this.onLocationChange(v)}> + + + + + + let addLeafsCheckbox = this.state.location!==""?
+
+

Add leaves inside?

+
+
+ this.onToggleCheckbox("hasLeaves", v)} + /> +
+
: ""; + + + if (!this.props.selectedGroups) { + radioButtonGroupHeader=""; + radioButtonGroup=""; + } + + if (this.props.selectedGroups.length===0){ + radioButtonGroup=""; + } + + if (this.props.addLeafs) { + numberOfGroups=""; + radioButtonGroupHeader=""; + radioButtonGroup=""; + addLeafsCheckbox=""; + } + + const dialog = ( + + {radioButtonGroupHeader} + {radioButtonGroup} + {numberOfGroups} + {addLeafsCheckbox} + {numberOfLeaves} + {conjoinOption} + {oddLeaf} + + ); + + return ( +
+ {dialog} +
+ ); + } + static propTypes = { + /** Dictionary of actions */ + action: PropTypes.objectOf(PropTypes.func), + /** Dictionary of selected groups where the key is the group ID and value is the group object */ + selectedGroups: PropTypes.arrayOf(PropTypes.string), + /** ID of project that the new groups will be added to */ + projectID: PropTypes.string, + /** `true` to have this component open */ + open: PropTypes.bool, + /** Callback to close this component open */ + closeDialog: PropTypes.func, + /** `true` to show Add Leafs Inside Group instead of Add Groups */ + addLeafs: PropTypes.bool, + } +} diff --git a/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.md b/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.md new file mode 100644 index 00000000..0be1ffc6 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/AddGroupDialog.md @@ -0,0 +1,16 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| numberOfGroups | number | number of new groups to add | +| hasLeaves | boolean | `true` if we want to add leaves | +| numberOfLeaves | number | number of leaves to add | +| conjoin | boolean | `true` to conjoin the leaves | +| oddLeaf | number | leaf number to leave out in conjoining | +| copies | number | number of copies of conjoined set of leaves to create | +| location | string | where to add the group(s) (`above`, `below` or `inside`) | +| errorText | object | dictionary of error messages for the text fields
numberOfGroups: string
numberOfLeaves: string
oddLeaf: string
copies: string| + + + + diff --git a/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js new file mode 100644 index 00000000..cdb040a7 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js @@ -0,0 +1,389 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; +import TextField from 'material-ui/TextField'; +import IconButton from 'material-ui/IconButton'; +import AddCircle from 'material-ui/svg-icons/content/add-circle'; +import RemoveCircle from 'material-ui/svg-icons/content/remove-circle-outline'; +import light from '../../../styles/light'; + +/** Dialog to add leaves in a collation. This component is used in the visual and tabular edit modes. */ +export default class AddLeafDialog extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + numberOfLeaves: 1, + conjoin: false, + oddLeaf: 2, + location: "", + errorText: { + numberOfLeaves: "", + oddLeaf: "", + }, + disabledAbove: false, + disabledBelow: false, + }; + } + + /** Open this modal component */ + handleOpen = () => { + this.setState({open: true}); + }; + + /** Close this modal component */ + handleClose = () => { + this.clearForm(); + this.setState({open: false}); + }; + + /** + * Increment a state's value by one, bounded by `max` and `min`. If the user previously + * entered an invalid value, the value is set to `min`. + * + * @param {string} name state name + * @param {number} min + * @param {number} max + * @public + */ + incrementNumber = (name, min, max) => { + let newCount = 0; + if (!this.isNormalInteger(this.state[name])) { + newCount = min; + } else { + newCount = Math.min(max, this.state[name]+1); + } + let newState = {errorText:{}}; + newState[name]=(isNaN(newCount))?1:newCount; + newState.errorText[name]=""; + this.setState({...newState}); + } + + /** + * Decrement a state by one, bounded by `max` and `min`. If the user previously + * entered an invalid value, the value is set to min. + * + * @param {string} name state name + * @param {number} min + * @param {number} max + * @public + */ + decrementNumber = (name, min, max) => { + let newCount = Math.min(max, Math.max(min, this.state[name]-1)); + let newState = {errorText:{}}; + newState[name]=(isNaN(newCount))?1:newCount; + newState.errorText[name]=""; + this.setState({...newState}); + } + + /** + * Validate user input. If invalid, display error message, otherwise update relevant state + * + * @param {string} name state name + * @param {number} value new input value + * @public + */ + onNumberChange = (stateName, value) => { + let errorState = this.state.errorText; + errorState[stateName] = ""; + if (!this.isNormalInteger(value)) { + errorState[stateName] = "Must be a number"; + } else { + value = parseInt(value, 10); + } + if (stateName==="numberOfLeaves" && (value<1 || value>999)) { + errorState[stateName] = "Number must be between 1 and 999"; + } else if (stateName==="oddLeaf" && (value<1 || value>this.state.numberOfLeaves)) { + errorState[stateName] = "Number must be between 1 and " + this.state.numberOfLeaves; + } + let newState = {}; + newState[stateName] = value; + this.setState({...newState, errorText: errorState}); + } + + /** + * Check if string is an integer + * + * @param {string} str + * @public + */ + isNormalInteger = (str) => { + return /^([1-9]\d*)$/.test(str); + } + + /** + * Toggle conjoin checkbox + * + * @param {boolean} value + * @public + */ + onToggleConjoin = (value) => { + this.setState({conjoin: value}); + } + + /** + * Update location radio button group + * + * @param {string} value + * @public + */ + onLocationChange = (value) => { + this.setState({location: value});; + } + + /** + * Return `true` if there are any errors in the input fields + * @public + */ + isDisabled = (activeLeaf) => { + let addable = activeLeaf.attached_above!=="None" && activeLeaf.attached_below!=="None"; + let numberOfLeavesError = !(this.state.errorText.numberOfLeaves===undefined) && this.state.errorText.numberOfLeaves.length>0; + let oddLeafError = !(this.state.errorText.oddLeaf===undefined) && this.state.errorText.oddLeaf.length>0; + return this.state.location==="" || addable || numberOfLeavesError || oddLeafError; + } + + /** + * Submit add leaf request + * @public + */ + submit = () => { + const leaf = this.props.Leafs[this.props.selectedLeaves[0]]; + let data = {leaf:{}, additional:{}}; + data["additional"]["noOfLeafs"] = this.state.numberOfLeaves; + data["additional"]["conjoin"] = (this.state.numberOfLeaves>1)? this.state.conjoin : false; + if (this.state.conjoin && this.state.numberOfLeaves>1 && !(this.state.numberOfLeaves%2===0)) { + data["additional"]["oddMemberLeftOut"] = this.state.oddLeaf; + } + let memberOrder = leaf.memberOrder; + if (this.state.location==="below") { + memberOrder += 1; + data["additional"]["order"] = leaf.order + 1; + } else { + data["additional"]["order"] = leaf.order; + } + + data["additional"]["memberOrder"] = memberOrder; + data["leaf"]["project_id"] = this.props.projectID; + data["leaf"]["parentID"] = leaf.parentID; + + this.props.action.addLeafs(data); + this.handleClose(); + this.clearForm(); + } + + /** + * Reset state + * @public + */ + clearForm = () => { + this.setState({ + numberOfLeaves: 1, + conjoin: false, + oddLeaf: 2, + location: "", + errorText: { + numberOfLeaves: "", + oddLeaf: "", + }, + disabledAbove: false, + disabledBelow: false, + }) + } + + render() { + const activeLeaf = this.props.Leafs[this.props.selectedLeaves[0]]; + let defaultAddLocation = ""; + + const actions = [ + , + , + ]; + + const styles = { + radioButton: { + marginBottom: 5, + }, + }; + + let noOfLeafs = ""; + let conjoinOption = ""; + let oddLeaf = ""; + + if (this.state.location!=="") { + noOfLeafs = +
+
+

Number of leaves

+
+
+ this.onNumberChange("numberOfLeaves", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} + /> + this.decrementNumber("numberOfLeaves", 1, 999)} + > + + + this.incrementNumber("numberOfLeaves", 1, 999)} + > + + +
+
; + } + + if (this.state.numberOfLeaves>1) { + conjoinOption = +
+
+

Conjoin leaves?

+
+
+ this.onToggleConjoin(v)} + checked={this.state.conjoin && this.state.numberOfLeaves>1} + style={{verticalAlign:"bottom"}} + /> +
+
+ } + if (this.state.conjoin && this.state.numberOfLeaves>1 && !(this.state.numberOfLeaves%2===0)) { + oddLeaf = +
+
+

Odd leaf to not conjoin

+
+
+ this.onNumberChange("oddLeaf", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} + /> + this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + > + + + this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + > + + +
+
+ } + + let disabledAddAbove = (activeLeaf.attached_above!=="None"? +
+
{this.setState({disabledAbove:true})}} + onMouseOut={()=>{this.setState({disabledAbove:false})}}> + above leaf {activeLeaf.order} +
+
+ Cannot insert a new leaf above leaf {activeLeaf.order} because leaf {activeLeaf.order} attached to leaf {activeLeaf.order-1} +
+
+ : "" + ); + let disabledAddBelow = (activeLeaf.attached_below!=="None"? +
+
{this.setState({disabledBelow:true})}} + onMouseOut={()=>{this.setState({disabledBelow:false})}}> + below leaf {activeLeaf.order} +
+
+ Cannot insert a new leaf below leaf {activeLeaf.order} because leaf {activeLeaf.order} is attached to leaf {activeLeaf.order+1} +
+
+ : "" + ); + + let addLeaves = +
+

Add new leaves...

+ {disabledAddBelow} + this.onLocationChange(v)} + > + + + + + {disabledAddAbove} +
+ + const dialog = ( + + {addLeaves} + {noOfLeafs} + {conjoinOption} + {oddLeaf} + + ); + + return ( +
+ + {dialog} +
+ ); + } + static propTypes = { + /** Dictionary of actions */ + action: PropTypes.objectOf(PropTypes.func), + /** Dictionary of selected groups where the key is the group ID and value is the group object */ + selectedLeaves: PropTypes.arrayOf(PropTypes.string), + /** ID of project that the new groups will be added to */ + projectID: PropTypes.string, + } +} diff --git a/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.md b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.md new file mode 100644 index 00000000..5031ca19 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.md @@ -0,0 +1,11 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| open | boolean | `true` to have this modal component open | +| numberOfLeaves | number | number of leaves to add | +| conjoin | boolean | `true` to conjoin the leaves | +| oddLeaf | number | leaf number to leave out in conjoining | +| copies | number | number of copies of conjoined set of leaves to create | +| location | string | where to add the leaf(s) (`above` or `below`) | +| errorText | object | dictionary of error messages for the text fields
numberOfLeaves: string
oddLeaf: string
copies: string| diff --git a/viscoll-app/src/components/infoBox/dialog/AddNote.js b/viscoll-app/src/components/infoBox/dialog/AddNote.js new file mode 100644 index 00000000..aae28b97 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/AddNote.js @@ -0,0 +1,202 @@ +import React from 'react'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import IconButton from 'material-ui/IconButton'; +import IconAdd from 'material-ui/svg-icons/content/add'; +import AutoComplete from 'material-ui/AutoComplete'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import TextField from 'material-ui/TextField'; + +/** Dialog to add a note to an object (leaf, side, or group). This component is used in the visual and tabular edit modes. */ +export default class AddNote extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + type: '', + description:'', + searchText: '', + noteID: null, + }; + } + + /** Open this modal component */ + handleOpen = () => { + this.setState({ + open: true, + type: '', + description:'', + searchText: '', + noteID: null, + }); + }; + + /** Close this modal component */ + handleClose = () => { + this.setState({open: false}); + }; + + handleUpdateInput = (searchText) => { + this.setState({ + searchText: searchText, + noteID: null + }); + }; + + handleNewRequest = (request) => { + // User pressed enter instead of selecting a note in drop down + // Look for key associated with user input + let noteID = null; + for (let id in this.props.Notes){ + const note = this.props.Notes[id]; + if (note.title===request) { console.log("here");noteID = note.id;} + } + this.setState({noteID}, ()=>{ + if (noteID) this.submit() + }); + }; + + submit = () => { + if (this.state.noteID!==null) { + // Attach existing note to selected objects + this.props.action.linkNote(this.state.noteID); + } else { + // Check if note exists (in case user types and did not press enter) + let noteID = null; + for (let id in this.props.Notes){ + const note = this.props.Notes[id]; + if (note.title===this.state.searchText) noteID = note.id; + } + if (noteID) { + this.props.action.linkNote(noteID); + } else { + // Did not find note, so create and attach new note to object + this.props.action.createAndAttachNote(this.state.searchText, this.state.type, this.state.description); + } + } + this.handleClose(); + } + + noteExists = () => { + for (let noteID in this.props.Notes) { + const note = this.props.Notes[noteID]; + if (note.title===this.state.searchText) { + return true; + } + } + return false; + } + + /** + * Mapping function to render one note type menu item + * @param {string} name note type name + * @public + */ + renderNoteTypes = (name) => { + return ; + } + + /** + * Update state on input change + * @param {string} name input name + * @param {string} value new value + * @public + */ + onChange = (name, value) => { + this.setState({[name]:value}); + } + + getFilteredNoteTitlesDropDown = () => { + return Object.keys(this.props.Notes).filter((noteID) => {return !this.props.commonNotes.includes(noteID)}) + } + + render() { + const dataSourceConfig = { + text: 'textKey', + value: 'valueKey', + }; + + const actions = [ + , + , + ]; + + let newNoteForm =
; + if (!this.noteExists() && this.state.searchText.length>1) { + newNoteForm = ( +
+ this.onChange("type",v)} + floatingLabelText="Note type" + fullWidth + style={{marginTop:-20}} + > + {this.props.noteTypes.map(this.renderNoteTypes)} + + this.onChange("description",v)} + multiLine + fullWidth + style={{marginTop:-20}} + /> +
+ ); + } + + let dialog = ( + {return {textKey: this.props.Notes[noteID].title, valueKey: noteID}})} + filter={(searchText, key) => (key.indexOf(searchText) !== -1)} + openOnFocus={true} + dataSourceConfig={dataSourceConfig} + fullWidth + listStyle={{ maxHeight: 300, overflow: 'auto' }} + errorText={(!this.noteExists()&&this.state.searchText.length>0)?"This note doesn't exist. To create and attach it, fill out its note type and description.":""} + errorStyle={{color:"#727272"}} + floatingLabelFocusStyle={{color:"#3A4B55"}} + /> + {newNoteForm} + ) + + return ( +
+ + + + {dialog} +
+ ); + } +} diff --git a/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js new file mode 100644 index 00000000..bb9f0235 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js @@ -0,0 +1,131 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; + +/** Delete confirmation dialog for deleting group(s) and leaf(s) */ +export default class DeleteConfirmationDialog extends React.Component { + state = { + open: false, + }; + + /** + * Open the dialog + * @public + */ + handleOpen = () => { + this.setState({open: true}); + }; + + /** + * Close the dialog + * @public + */ + handleClose = () => { + this.setState({open: false}); + }; + + containsTacketedLeaf = () => { + if (this.props.memberType==="Leaf") { + for (const leafID of this.props.selectedObjects) { + if (this.props.Groups[this.props.Leafs[leafID].parentID].tacketed===leafID) + return true; + } + } + return false; + } + + /** + * Generate text depending of the type and number of selected object(s) + * @public + */ + getTitle = () => { + const memberType = this.props.memberType; + const item = this.props[memberType+"s"][this.props.selectedObjects[0]]; + if (item){ + if (this.containsTacketedLeaf()) { + if (this.props.selectedObjects.length>1) { + return "One of the selected leaves is tacketed. You cannot delete tacketed leaves."; + } else { + return "You cannot delete a leaf that is tacketed."; + } + } else if (this.props.selectedObjects.length===1) { + return "Are you sure you want to delete " + item.memberType.toLowerCase() + " " + item.order + "?"; + } else { + return "Are you sure you want to delete " + + this.props.selectedObjects.length + " " + item.memberType.toLowerCase() + "s?"; + } + } + + } + + /** + * Submit delete request and close dialog + * @public + */ + submit = () => { + if (this.props.selectedObjects.length===1) { + // handle single delete + let id = this.props.selectedObjects[0] + this.props.action.singleDelete(id); + } else { + // handle batch delete + const memberType = this.props.memberType.toLowerCase(); + let data = {}; + data[memberType+"s"]= []; + for (var id of this.props.selectedObjects) { + data[memberType+"s"].push(id); + } + this.props.action.batchDelete(data); + } + this.handleClose(); + } + + render() { + const actions = [ + , + , + ]; + + return ( +
+ + + +
+ ); + } + static propTypes = { + /** `true` to have the Delete button span the whole width of its parent container */ + fullWidth: PropTypes.bool, + /** Dictionary of actions */ + action: PropTypes.object, + /** Dictionary of selected objects to delete */ + selectedObjects: PropTypes.arrayOf(PropTypes.string), + } +} diff --git a/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.md b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.md new file mode 100644 index 00000000..3ee85104 --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.md @@ -0,0 +1,5 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| open | boolean | `true` to have this modal component open | \ No newline at end of file diff --git a/viscoll-app/src/components/infoBox/dialog/NoteDialog.js b/viscoll-app/src/components/infoBox/dialog/NoteDialog.js new file mode 100644 index 00000000..48cd289d --- /dev/null +++ b/viscoll-app/src/components/infoBox/dialog/NoteDialog.js @@ -0,0 +1,113 @@ +import React from 'react'; +import EditNoteForm from '../../notesManager/EditNoteForm'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; + +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.Groups[value].order}`; + 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.Leafs[value].order}`; + 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.Leafs[this.props.Rectos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Recto}`; + sidesWithCurrentNote.push({label, value}) + } + for (let value of versosWithCurrentNote){ + const leafOrder = this.props.Leafs[this.props.Versos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Verso}`; + sidesWithCurrentNote.push({label, value}) + } + return sidesWithCurrentNote; + } + + getRectosAndVersos = () => { + const size = Object.keys(this.props.Rectos).length; + let result = {}; + for (let i=0; i, + ]; + return ( + + + + ); + } + +} + + + + diff --git a/viscoll-app/src/components/notesManager/DeleteConfirmation.js b/viscoll-app/src/components/notesManager/DeleteConfirmation.js new file mode 100644 index 00000000..54b5bf6a --- /dev/null +++ b/viscoll-app/src/components/notesManager/DeleteConfirmation.js @@ -0,0 +1,100 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; +import IconDelete from 'material-ui/svg-icons/action/delete'; +import IconButton from 'material-ui/IconButton'; + +/** Delete confirmation dialog for deleting notes and note types */ +export default class DeleteConfirmation extends React.Component { + state = { + open: false, + }; + /** + * Open the dialog + * @public + */ + handleOpen = () => { + this.setState({open: true}); + }; + /** + * Close the dialog + * @public + */ + handleClose = () => { + this.setState({open: false}); + }; + + submit = () => { + if (this.props.item==="note") { + this.props.action.deleteNote(this.props.noteID); + } else { + this.props.onDelete(this.props.index) + } + this.handleClose(); + } + + render() { + const actions = [ + , + , + ]; + let deleteIcon =
+ +
+ let message = "This note will be removed from all groups/sides/leaves that have this note."; + if (this.props.item==="note type") { + deleteIcon = + + + message = "Any existing notes associated with this note type will be assigned to the note type 'Unknown'."; + } + if (this.props.item!=="") { + return ( +
+ {deleteIcon} + + {message} + +
+ ); + } else { + return
; + } + } + static propTypes = { + /** `true` to have the Delete button span the whole width of its parent container */ + fullWidth: PropTypes.bool, + /** Dictionary of actions */ + action: PropTypes.object, + /** Dictionary of selected objects to delete */ + selectedObjects: PropTypes.object, + } +} \ No newline at end of file diff --git a/viscoll-app/src/components/notesManager/EditNoteForm.js b/viscoll-app/src/components/notesManager/EditNoteForm.js new file mode 100644 index 00000000..378c0180 --- /dev/null +++ b/viscoll-app/src/components/notesManager/EditNoteForm.js @@ -0,0 +1,465 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import DeleteConfirmation from './DeleteConfirmation'; +import TextField from 'material-ui/TextField'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; +import Chip from 'material-ui/Chip'; +import MultiSelectAutoComplete from '../../helpers/MultiSelectAutoComplete'; +import Checkbox from 'material-ui/Checkbox'; + + +/** Create New Note tab in the Note Manager */ +export default class EditNoteForm extends Component { + constructor(props) { + super(props); + this.state = { + id: props.note.id, + title: props.note.title, + type: props.note.type, + description: props.note.description, + linkType: "", + editing: { + title: false, + description: false, + }, + errors: { + title: "", + } + }; + } + + + componentWillReceiveProps(nextProps) { + this.setState({ + id: nextProps.note.id, + title: nextProps.note.title, + type: nextProps.note.type, + description: nextProps.note.description, + linkType: "", + editing: { + title: false, + description: false, + }, + errors: { + title: "", + } + }) + } + + + /** + * Validates title + * @param {string} title + * @public + */ + validateTitle = (title) => { + for (let noteID in this.props.Notes) { + const note = this.props.Notes[noteID]; + if (note.title===title && note.id!==this.state.id) { + this.setState({errors:{title:"This note title already exists."}}); + return; + }; + } + if (title.length>100) { + this.setState({errors:{title:"Title must be less than 100 characters."}}); + } else if (title.length===0) { + this.setState({errors:{title:"Title must not be empty."}}); + } else { + this.setState({errors:{title:""}}); + } + } + + /** + * Update state on input change + * @param {string} name input name + * @param {string} value new value + * @public + */ + onChange = (name, value) => { + this.setState({[name]:value, editing: {...this.state.editing, [name]: true}}); + if (name==="title") this.validateTitle(value.trim()); + if (name==="type") { + let editing = { + title: this.state.title, + type: value, + description: this.state.description, + } + if (this.props.note) + this.props.action.updateNote(this.props.note.id, editing); + } + } + + /** + * Update new note + * @public + */ + update = (event, name) => { + event.preventDefault(); + if (this.props.note) { + let editing = { + title: this.state.title, + type: this.state.type, + description: this.state.description, + } + this.setState({editing: {...this.state.editing, [name]:false}}); + this.props.action.updateNote(this.props.note.id, editing); + } + } + + /** + * Reset input field to original value + * @param {string} name input field name + * @public + */ + onCancelUpdate = (name) => { + this.setState({ + [name]: this.props.note[name], + editing: { + ...this.state.editing, + [name]: false + }, + errors: { + ...this.state.errors, + [name]: "" + } + }); + } + + + /** + * Mapping function to render one note type menu item + * @param {string} name note type name + * @public + */ + renderNoteTypes = (name) => { + return ; + } + + + renderMenuItem = (itemID, type, index) => { + const item = this.props[type+"s"][itemID]; + let label = `${type} ${item.order}`; + if (type==="Side") { + const leaf = this.props.Leafs[item.parentID]; + let sideName = item.memberType; + label = `Leaf ${leaf.order}: ${type} ${sideName}` + } + return ( +
+ {label} +
+ ); + } + + getCurrentValues = (type) => { + let resultIDs; + switch (type) { + case "Group": + resultIDs = this.props.linkedGroups.map((item)=>{return item.value}) + break; + case "Leaf": + resultIDs = this.props.linkedLeaves.map((item)=>{return item.value}) + break; + case "Side": + resultIDs = this.props.linkedSides.map((item)=>{return item.value}) + break; + default: + break; + } + return resultIDs; + } + + + getNonIntersectingItems = (newList, oldList) => { + let newListUniqueItems = newList.filter((item)=>{return !oldList.includes(item)}); + let oldListUniqueItems = oldList.filter((item)=>{return !newList.includes(item)}); + return [...newListUniqueItems, ...oldListUniqueItems] + } + + + updatedObjects = (values, type) => { + let objIDs = []; + for (let item of values) { + objIDs.push(item.value) + } + let currentValues = this.getCurrentValues(type); + let diff = this.getNonIntersectingItems(objIDs, currentValues); + let objectsToUnlink = []; + let objectsToLink = []; + for (let objID of diff) { + if (currentValues.includes(objID)){ + objectsToUnlink.push({id:objID, type}) + } + if (objIDs.includes(objID)){ + objectsToLink.push({id:objID, type}) + } + } + let linkedObjects; + switch (type) { + case "Group": + linkedObjects = "linkedGroups" + break; + case "Leaf": + linkedObjects = "linkedLeaves" + break; + case "Side": + linkedObjects = "linkedSides" + break; + default: + break; + } + if (diff.length > 0){ + this.setState({[linkedObjects]: values}, ()=>{ + this.props.action.linkAndUnlinkNotes(this.props.note.id, objectsToLink, objectsToUnlink); + }); + } + } + + + /** + * Return a generated HTML of submit and cancel buttons for a specific input name + * @param {string} name name of input field + * @public + */ + renderSubmitButtons = (name) => { + if (this.state.editing[name] && this.props.note!==null && this.props.note!==undefined) { + return ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + name="submit" + type="submit" + disabled={name==="title" && this.state.errors.title!==""} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.onCancelUpdate(name)} + /> +
+ ) + } else { + return ""; + } + } + + /** + * Render all group, leaf and side chips that this note is attached to + * @public + */ + renderChips = () => { + let chips = []; + if (this.props.note) { + for (let key of this.props.note.objects.Group) { + chips.push(this.renderChip("Group", key)); + } + for (let key of this.props.note.objects.Leaf) { + chips.push(this.renderChip("Leaf", key)); + } + for (let key of this.props.note.objects.Recto) { + chips.push(this.renderChip("Side", key)); + } + for (let key of this.props.note.objects.Verso) { + chips.push(this.renderChip("Side", key)); + } + } + return chips; + } + + /** + * Render a single chip + * @param {string} type + * @param {array} order [object order, leaf order (if object is a side)] + * @public + */ + renderChip = (type, id) => { + let name; + if (type==="Side") { + if (this.props.Rectos.hasOwnProperty(id)){ + const recto = this.props.Rectos[id]; + const leafOrder = this.props.Leafs[recto.parentID].order + name = `Leaf ${leafOrder}: Side Recto`; + type = "Recto" + } else { + const verso = this.props.Versos[id]; + const leafOrder = this.props.Leafs[verso.parentID].order + name = `Leaf ${leafOrder}: Side Verso`; + type = "Verso" + } + } else if (type==="Group"){ + const group = this.props.Groups[id]; + name = `Group ${group.order}`; + } else { + const leaf = this.props.Leafs[id]; + name = `Leaf ${leaf.order}`; + } + let deleteFn = ()=>this.props.action.unlinkNote(this.props.note.id, [{type, id}]); + if (this.props.isReadOnly) deleteFn = null; + return ( + {}} + > + {name} + + ); + } + + + render() { + let title = this.props.isReadOnly? this.props.note.title : "Edit " + this.props.note.title; + let linkedObjects = ""; + let deleteButton = ""; + let chips = this.renderChips(); + let objectDropDown = ""; + if (this.state.linkType==="Group") { + objectDropDown = ( +
+ this.updatedObjects(selected, "Group")} + selectedItems={this.props.linkedGroups} + > + {Object.keys(this.props.Groups).map((itemID)=>this.renderMenuItem(itemID, "Group"))} + +
+ ); + } else if (this.state.linkType==="Leaf") { + objectDropDown = ( +
+ this.updatedObjects(selected, "Leaf")} + selectedItems={this.props.linkedLeaves} + > + {Object.keys(this.props.Leafs).map((itemID)=>this.renderMenuItem(itemID, "Leaf"))} + +
+ ); + } else if (this.state.linkType==="Side") { + objectDropDown = ( +
+ this.updatedObjects(selected, "Side")} + selectedItems={this.props.linkedSides} + > + {Object.keys(this.props.Sides).map((itemID, index)=>this.renderMenuItem(itemID, "Side", index))} + +
+ ); + } + if (this.props.note) { + linkedObjects = ( +
+ { chips.length>0? +
+

Attached to

+
+
+ {chips} +
+
+
+ : "" + } + {this.props.isReadOnly?"":
+

Attach a new item

+ this.setState({linkType:v})} + value={this.state.linkType} + style={{marginTop:"-2em",width:120}} + > + {["Group", "Leaf", "Side"].map((type) => { + return ; + })} + + {objectDropDown} +
+ } +
); + deleteButton = this.props.isReadOnly?"":
+ +
+ } + return ( +
+

{title}

+
+
+ Title +
+
+ {this.props.isReadOnly?
{this.state.title}
: +
this.update(e, "title")}> + this.onChange("title",v)} + fullWidth + /> + {this.renderSubmitButtons("title")} + } +
+
+ Type +
+
+ {this.props.isReadOnly?
{this.state.type}
: + this.onChange("type",v)} + > + {this.props.noteTypes.map(this.renderNoteTypes)} + } +
+
+ Description +
+
+ {this.props.isReadOnly?
{this.state.description}
: +
this.update(e, "description")}> + this.onChange("description",v)} + multiLine + fullWidth + /> + {this.renderSubmitButtons("description")} + } +
+
+ Show in diagram +
+
+ this.props.action.updateNote(this.props.note.id, {title:this.state.title,type:this.state.type,description:this.state.description,show:v})} + /> +
+ {linkedObjects} + {deleteButton} +
+
+ ); + } + static propTypes = { + /** Active project ID */ + projectID: PropTypes.string + } +} diff --git a/viscoll-app/src/components/notesManager/ManageNotes.js b/viscoll-app/src/components/notesManager/ManageNotes.js new file mode 100644 index 00000000..f452aad0 --- /dev/null +++ b/viscoll-app/src/components/notesManager/ManageNotes.js @@ -0,0 +1,208 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import Drawer from 'material-ui/Drawer'; +import EditNoteForm from "./EditNoteForm"; +import NewNoteForm from "./NewNoteForm"; +import Add from "material-ui/svg-icons/content/add" + +/** Create New Note tab in the Note Manager */ +export default class ManageNotes extends Component { + constructor(props) { + super(props); + this.state = { + activeNote: null, + title: "", + type: "", + description: "", + }; + } + + /** + * Update state when user clicks on new note item + * @param {number} index note index in the list of notes + * @public + */ + onItemChange = (activeNote) => { + this.setState({activeNote}); + } + + componentWillReceiveProps(nextProps) { + if (this.state.activeNote) + this.setState({activeNote: nextProps.Notes[this.state.activeNote.id]}); + } + + /** + * Mapping function to render a note thumbnail + * @public + */ + renderList = (noteID) => { + const note = this.props.Notes[noteID]; + return ( +
this.onItemChange(note)} + > + {note.title} +
+ ); + } + + /** + * Clear values in the input fields + * @public + */ + reset = () => { + this.setState({ + title: "", + type: "", + description: "", + }); + } + + deleteNote = (noteID) => { + this.props.action.deleteNote(noteID); + this.setState({activeNote: null}); + } + + updateNote = (noteID, note) => { + this.props.action.updateNote(noteID, note); + } + + linkNote = (noteID, object) => { + this.props.action.linkNote(noteID, object); + } + + unlinkNote = (noteID, object) => { + this.props.action.unlinkNote(noteID, object); + } + + linkAndUnlinkNotes = (noteID, linkObjects, unlinkObjects) => { + this.props.action.linkAndUnlinkNotes(noteID, linkObjects, unlinkObjects); + } + + getLinkedGroups = () => { + const groupsWithCurrentNote = Object.keys(this.props.Groups).filter((groupID) => { + return (this.props.Groups[groupID].notes.includes(this.state.activeNote.id)) + }); + return groupsWithCurrentNote.map((value) => { + const label = `Group ${this.props.Groups[value].order}`; + return {label, value}; + }); + } + + getLinkedLeaves = () => { + const leafsWithCurrentNote = Object.keys(this.props.Leafs).filter((leafID) => { + return (this.props.Leafs[leafID].notes.includes(this.state.activeNote.id)) + }); + return leafsWithCurrentNote.map((value)=>{ + const label = `Leaf ${this.props.Leafs[value].order}`; + return {label, value}; + }); + } + + getLinkedSides = () => { + const rectosWithCurrentNote = Object.keys(this.props.Rectos).filter((rectoID) => { + return (this.props.Rectos[rectoID].notes.includes(this.state.activeNote.id)) + }); + const versosWithCurrentNote = Object.keys(this.props.Versos).filter((versoID) => { + return (this.props.Versos[versoID].notes.includes(this.state.activeNote.id)) + }); + const sidesWithCurrentNote = []; + for (let value of rectosWithCurrentNote){ + const leafOrder = this.props.Leafs[this.props.Rectos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Recto}`; + sidesWithCurrentNote.push({label, value}) + } + for (let value of versosWithCurrentNote){ + const leafOrder = this.props.Leafs[this.props.Versos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Verso}`; + sidesWithCurrentNote.push({label, value}) + } + return sidesWithCurrentNote; + } + + getRectosAndVersos = () => { + const size = Object.keys(this.props.Rectos).length; + let result = {}; + for (let i=0; i + ); + } else{ + noteForm = ( + + ); + } + + + + return ( +
+ +
this.onItemChange(null)} + > + Create new note + +
+ {Object.keys(this.props.Notes).map(this.renderList)} +
+
+ {noteForm} +
+
+ ); + } + static propTypes = { + /** Active project ID */ + projectID: PropTypes.string + } +} diff --git a/viscoll-app/src/components/notesManager/NewNoteForm.js b/viscoll-app/src/components/notesManager/NewNoteForm.js new file mode 100644 index 00000000..ffd2d527 --- /dev/null +++ b/viscoll-app/src/components/notesManager/NewNoteForm.js @@ -0,0 +1,197 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import TextField from 'material-ui/TextField'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import RaisedButton from 'material-ui/RaisedButton'; + + +/** Create New Note tab in the Note Manager */ +export default class NewNoteForm extends Component { + constructor(props) { + super(props); + this.state = { + title: "", + type: "", + description: "", + errors: { + title: "", + } + }; + } + + /** + * Validates title + * @param {string} title + * @public + */ + validateTitle = (title) => { + for (let noteID in this.props.Notes) { + const note = this.props.Notes[noteID]; + if (note.title===title && note.id!==this.state.id) { + this.setState({errors:{title:"This note title already exists."}}); + return; + }; + } + if (title.length>100) { + this.setState({errors:{title:"Title must be less than 100 characters."}}); + } else if (title.length===0) { + this.setState({errors:{title:"Title must not be empty."}}); + } else { + this.setState({errors:{title:""}}); + } + } + + /** + * Update state on input change + * @param {string} name input name + * @param {string} value new value + * @public + */ + onChange = (name, value) => { + this.setState({[name]:value, editing: {...this.state.editing, [name]: true}}); + if (name==="title") this.validateTitle(value.trim()); + if (name==="type") { + let editing = { + title: this.state.title, + type: value, + description: this.state.description, + } + if (this.props.note) + this.props.action.updateNote(this.props.note.id, editing); + } + } + + /** + * Create new note + * @public + */ + create = () => { + let note = { + "project_id": this.props.projectID, + "title": this.state.title, + "type": this.state.type, + "description": this.state.description, + } + this.props.action.addNote(note); + // Reset form + this.setState({ + title: "", + type: "", + description: "", + errors: { + title: "", + } + }); + } + + /** + * Clear values in the input fields if we are creating a new note + * Reset to original values if we are editing an existing note + * @param {object} props + * @public + */ + reset = (props) => { + this.setState({ + title: "", + type: "", + description: "", + errors: { + title:"", + type:"", + description:"", + } + }); + } + + + /** + * Mapping function to render one note type menu item + * @param {string} name note type name + * @public + */ + renderNoteTypes = (name) => { + return ; + } + + + renderMenuItem = (item, type, index) => { + let label = `${type} ${item.order}`; + if (type==="Side") { + let sideName = item.order===0 ? "Recto" : "Verso"; + label = `Leaf ${Math.ceil((index-3)/2)}: ${type} ${sideName}` + } + return ( +
+ {label} +
+ ); + } + + render() { + let title = "Create a new note"; + let createButtons =
+ this.create()} + disabled={this.state.errors.title!=="" || this.state.type==="" || this.state.title===""} + /> +   + this.reset()} + style={{width:120}} + /> +
+ + return ( +
+

{title}

+
+
+ Title +
+
+ this.onChange("title",v)} + fullWidth + /> +
+
+ Type +
+
+ this.onChange("type",v)} + > + {this.props.noteTypes.map(this.renderNoteTypes)} + +
+
+ Description +
+
+ this.onChange("description",v)} + multiLine + fullWidth + /> +
+ {createButtons} +
+
+ ); + } + static propTypes = { + /** Active project ID */ + projectID: PropTypes.string, + } +} diff --git a/viscoll-app/src/components/notesManager/NoteType.js b/viscoll-app/src/components/notesManager/NoteType.js new file mode 100644 index 00000000..aa663de6 --- /dev/null +++ b/viscoll-app/src/components/notesManager/NoteType.js @@ -0,0 +1,217 @@ +import React, {Component} from 'react'; +import DeleteConfirmation from './DeleteConfirmation'; +import RaisedButton from 'material-ui/RaisedButton'; +import TextField from 'material-ui/TextField'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; + +/** Note type page to add, edit and delete note types */ +export default class NoteType extends Component { + + constructor(props) { + super(props); + this.state = { + newType: "", + types: [...props.noteTypes], + editing: new Array(props.noteTypes.length).fill(false), + errorNewType: "", + errorTypes: new Array(props.noteTypes.length).fill(""), + lastSubmitted: -2, + } + } + + componentWillReceiveProps(nextProps) { + if (this.state.types.length !== nextProps.noteTypes.length) { + this.setState({types: [...nextProps.noteTypes]}); + this.resetEditing(); + } + } + + resetEditing = () => { + this.setState({editing: new Array(this.props.noteTypes.length).fill(false), newType: ""}) + } + + onNewTypeChange = (newType) => { + // newType = newType.trim(); + this.setState({newType}, ()=>{ + if (!this.isValid(newType.trim())){ + let errorMessage = `Note type with name ${newType} already exists in this project`; + if (newType.length===0) + errorMessage = ""; + this.setState({errorNewType: errorMessage}); + } else { + this.setState({errorNewType: ""}); + } + }); + } + + onChange = (newType, index) => { + this.setType(index, newType); + this.setEditing(index, true); + if (!this.isValid(newType)){ + let errorMessage = `Note type with name ${newType} already exists in this project`; + if (newType===this.props.noteTypes[index]) + errorMessage = ""; + if (newType.length===0) + errorMessage = `Note type cannot be blank`; + this.setError(index, errorMessage); + } else { + this.setError(index, ""); + } + } + + onUpdate = (event, index) => { + event.preventDefault(); + const newNoteType = this.state.types[index]; + if (newNoteType!==this.props.noteTypes[index]) { + this.setState({lastSubmitted: index}); + let noteType = { + project_id: this.props.projectID, + type: newNoteType, + old_type: this.props.noteTypes[index], + } + this.props.action.updateNoteType(noteType); + } + this.setEditing(index, false); + } + + isValid = (newType) => { + return !this.props.noteTypes.includes(newType) && newType.length!==0; + } + + onDelete = (index) => { + let noteType = { + project_id: this.props.projectID, + type: this.state.types[index], + } + let updatedEditing = [...this.state.editing]; + updatedEditing.splice(index, 1); + this.setState({editing: updatedEditing}, ()=>{ + this.props.action.deleteNoteType(noteType); + }); + } + + onCreate = (event) => { + event.preventDefault(); + let noteType = { + project_id: this.props.projectID, + type: this.state.newType.trim(), + } + this.props.action.createNoteType(noteType); + this.resetEditing(); + this.setState({lastSubmitted: -1}); + } + + onCancelUpdate = (index) => { + this.setType(index, this.props.noteTypes[index]); + this.setError(index, ""); + this.setEditing(index, false); + } + + setType = (index, value) => { + let newTypes = [...this.state.types]; + newTypes[index]=value; + this.setState({types: newTypes}); + } + + setEditing = (index, value) => { + let newEditing = [...this.state.editing]; + newEditing[index]=value; + this.setState({editing: newEditing}); + } + + setError = (index, value) => { + let newErrors = [...this.state.errorTypes]; + newErrors[index] = value; + this.setState({errorTypes: newErrors}); + } + + /** + * Return a generated HTML of submit and cancel buttons for a specific input name + * @param {number} index index of note type + * @public + */ + renderSubmitButtons = (index) => { + if (this.state.editing[index]) { + return ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + name="submit" + type="submit" + disabled={!this.isValid(this.state.types[index])} + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.onCancelUpdate(index)} + /> +
+ ) + } else { + return ""; + } + } + + renderNoteType = (noteType, index) => { + return ( +
+
this.onUpdate(e, index)}> + this.onChange(v,index)} + errorText={this.state.errorTypes[index]} + style={{width:"75%"}} + /> + { noteType==="Unknown"? "" : + + } + {this.renderSubmitButtons(index)} + +
+ ); + } + + filterNoteType = (object, index) => { + return object.key!=="type_0"; + } + + render() { + return
+

Add a new note type

+
this.onCreate(e)}> +
+
+ this.onNewTypeChange(v)} + errorText={this.state.errorNewType} + style={{width: 300}} + /> +
+
+ +
+
+
+

Your note types

+
+ {this.props.noteTypes.map(this.renderNoteType).filter(this.filterNoteType)} +
+
; + } +} diff --git a/viscoll-app/src/components/notesManager/NotesFilter.js b/viscoll-app/src/components/notesManager/NotesFilter.js new file mode 100644 index 00000000..42e84aa1 --- /dev/null +++ b/viscoll-app/src/components/notesManager/NotesFilter.js @@ -0,0 +1,56 @@ +import React, {Component} from 'react'; +import TextField from 'material-ui/TextField'; +import Checkbox from 'material-ui/Checkbox'; + +/** Filter notes */ +class NotesFilter extends Component { + + constructor(props) { + super(props); + this.state = { + value: "", + } + } + + render() { + return ( +
+
+ {this.setState({value});this.props.onValueChange(e,value)}} + style={{marginLeft:10,marginRight:10}} + value={this.state.value} + /> +
+
0)?"searchOptions active":"searchOptions"}> + this.props.onTypeChange("title", checked)} + /> + this.props.onTypeChange("type", checked)} + /> + this.props.onTypeChange("description", checked)} + /> +
+
+ ); + } +} + + +export default NotesFilter; diff --git a/viscoll-app/src/components/topbar/UserProfileForm.js b/viscoll-app/src/components/topbar/UserProfileForm.js new file mode 100644 index 00000000..95b50118 --- /dev/null +++ b/viscoll-app/src/components/topbar/UserProfileForm.js @@ -0,0 +1,492 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TextField from 'material-ui/TextField'; +import Dialog from 'material-ui/Dialog'; +import RaisedButton from 'material-ui/RaisedButton'; +import FlatButton from 'material-ui/FlatButton'; +import IconSubmit from 'material-ui/svg-icons/action/done'; +import IconClear from 'material-ui/svg-icons/content/clear'; + +/** + * Form to edit user account information + */ +class UserProfileForm extends React.Component { + constructor(props) { + super(props); + this.state = { + name: props.name, + email: props.email, + emailMessagePending: false, + emailMessage: false, + currentPassword: "", + newPassword: "", + newPasswordConfirm: "", + errors: { + name: "", + email: "", + newPassword: "", + newPasswordConfirm: "", + currentPassword: "" + }, + editing: { + name: false, + email: false, + newPassword: false, + newPasswordConfirm: false, + currentPassword: false, + }, + deleteDialog: false, + unsavedDialog: false, + }; + } + + + componentWillReceiveProps(nextProps) { + this.setState({ + name: nextProps.name, + errors: { + ...this.state.errors, + currentPassword: nextProps.currentPasswordError.toString(), + email: nextProps.emailTakenError.toString(), + }, + currentPassword: "", + newPassword: "", + newPasswordConfirm: "", + unsavedDialog: false, + changed: false, + }, () => { + if (this.state.emailMessagePending && this.state.errors.email === "") { + this.setState({emailMessage:true,emailMessagePending:false}); + } + }); + } + + /** + * Validate user input and display appropriate error message + * @param {string} type text field name + * @public + */ + checkValidationError = () => { + const errors = {}; + // Validate password + if (this.state.editing.currentPassword || this.state.newPassword) { + if (!this.state.currentPassword) { + errors.currentPassword = "Current password cannot be blank"; + } else { + errors.currentPassword = ""; + } + if (!this.state.newPasswordConfirm) { + errors.newPasswordConfirm = "New password confirmation cannot be blank"; + } else if (this.state.newPassword !== this.state.newPasswordConfirm) { + errors.newPasswordConfirm = "Password confirmation does not match new password"; + } else { + errors.newPasswordConfirm = ""; + } + if (!this.state.newPassword) { + errors.newPassword = "New password cannot be blank"; + } else { + errors.newPassword = ""; + } + } + // Validate name + if (this.state.editing.name && !this.state.name) { + errors.name = "Name cannot be blank"; + } else { + errors.name = ""; + } + // Validate email + if (this.state.editing.email) { + let re = /[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/igm; + if (!this.state.email) { + errors.email = "Email cannot be blank"; + } else if (!re.test(this.state.email)) { + errors.email = "Invalid email address"; + } else { + errors.email = ""; + } + } + return errors; + } + + /** + * Return true if any errors exist in the project form + * @public + */ + ifErrorsExist = (type) => { + return (this.state.errors[type]!==""); + } + + /** + * Return true if any input fields have been changed and not saved + * @public + */ + isEditing = () => { + return (this.state.editing.name || this.state.editing.email || this.state.editing.currentPassword || this.state.editing.newPassword || this.state.editing.newPasswordConfirm); + } + + /** + * Update state when user inputs new value in a text field + * @param {string} event + * @param {string} newValue new value + * @param {string} type text field name + * @public + */ + onInputChange = (newValue, type) => { + this.setState({[type]: newValue, editing: {...this.state.editing, [type]:true}}, () => { + this.setState({errors: {...this.state.errors, ...this.checkValidationError()}}) + }); + } + + /** + * Toggle delete confirmation dialog + * @param {boolean} deleteDialog show dialog? + * @public + */ + handleDeleteDialogToggle = (deleteDialog=false) => { + this.setState({ deleteDialog }); + }; + + /** + * Show ignore changes dialog or close user profile, depending on parameter + * @param {boolean} ignoreChanges show ignore changes dialog? + * @public + */ + handleUserProfileClose = (ignoreChanges=false) => { + // Check for any unsaved changes before closing and show the warning dialog. + if (this.isEditing() && !ignoreChanges){ + this.setState({ unsavedDialog: true }); + } + else { + this.setState({ + name: this.props.name, + email: this.props.email, + currentPassword: "", + newPassword: "", + newPasswordConfirm: "", + errors: { + name: "", + email: "", + newPassword: "", + newPasswordConfirm: "", + currentPassword: "" + }, + editing: { + name: false, + email: false, + newPassword: false, + newPasswordConfirm: false, + currentPassword: false, + } + }, () => this.props.toggleUserProfile()); + + } + } + + /** + * Delete user account + * @public + */ + handleUserAccountDelete = () => { + this.props.toggleUserProfile(false); + this.props.handleUserAccountDelete(); + } + + /** + * Toggle unsaved dialog + * @public + */ + handleUnsavedDialogToggle = (unsavedDialog=false) => { + this.setState({ unsavedDialog }); + }; + + /** + * Reset input field to original value + * @param {string} type text field name + * @public + */ + handleCancelUpdate = (type) => { + if (type==="currentPassword") { + this.setState({ + currentPassword:"", + newPassword:"", + newPasswordConfirm:"", + editing: { + ...this.state.editing, + currentPassword: false, + newPassword: false, + newPasswordConfirm: false, + }, + errors: { + ...this.state.errors, + currentPassword:"", + newPassword:"", + newPasswordConfirm:"", + } + }); + } else { + this.setState({ + [type]: this.props[type], + editing: { + ...this.state.editing, + [type]: false, + }, + errors: { + ...this.state.errors, + [type]: "", + } + }) + } + } + + /** + * Return a generated HTML of submit and cancel buttons for a specific input name + * @param {string} type text field name + * @public + */ + submitButtons = (type) => { + if (this.state.editing[type]) { + return ( +
+ } + style={{minWidth:"60px",marginLeft:"5px"}} + name="submit" + type="submit" + disabled={type==="currentPassword"? (this.ifErrorsExist("currentPassword")||this.ifErrorsExist("newPassword")||this.ifErrorsExist("newPasswordConfirm")) : this.ifErrorsExist(type) } + /> + } + style={{minWidth:"60px",marginLeft:"5px"}} + onTouchTap={(e)=>this.handleCancelUpdate(type)} + /> +
+ ) + } else { + return ""; + } + } + + /** + * Update a field in user + * @param {object} event + * @param {string} type text field name + * @public + */ + handleUserUpdate = (event, type) => { + event.preventDefault(); + let updatedUser = { + user: { + [type]: this.state[type], + } + }; + if (this.state.currentPassword!=="") { + updatedUser = {user: { + current_password: this.state.currentPassword, + password: this.state.newPassword + }}; + } + this.props.handleUserProfileUpdate(updatedUser); + let types = {}; + if (type==="password") { + types = {currentPassword: false, newPassword: false, newPasswordConfirm: false}; + } else { + types = {[type]: false}; + } + this.setState({editing:{...this.state.editing, ...types}}, ()=> { + if (type==="email") { + this.setState({emailMessagePending:true}); + } + }); + } + + render() { + const userProfileActions = [ + this.handleUserProfileClose()} + />, + this.handleDeleteDialogToggle(true)} + style={{float: 'left'}} + /> + ]; + + const deleteActions = [ + this.handleDeleteDialogToggle()} + />, + , + ]; + + const unsaveActions = [ + this.handleUnsavedDialogToggle()} + />, + this.handleUserProfileClose(true)} + />, + ]; + + const emailConfirmActions = [ + this.setState({emailMessage:false})} + /> + ]; + + let nameField = ( +
+
this.handleUserUpdate(e, "name")}> + this.onInputChange(v, "name")} + name="name" + floatingLabelText="Name" + errorText={this.state.errors.name} + value={this.state.name} + fullWidth + /> + {this.submitButtons("name")} + +
+ ); + + let emailField = ( +
+
this.handleUserUpdate(e, "email")}> + this.onInputChange(v, "email")} + name="email" + floatingLabelText="E-mail" + errorText={this.state.errors.email} + value={this.state.email} + fullWidth + /> + {this.submitButtons("email")} + +
+ ); + + let password = ( +
+
this.handleUserUpdate(e, "password")}> + this.onInputChange(v, "currentPassword")} + name="currentPassword" + floatingLabelText="Current Password" + errorText={this.state.errors.currentPassword} + type="password" + value={this.state.currentPassword} + fullWidth + /> + this.onInputChange(v, "newPassword")} + name="newPassword" + floatingLabelText="New Password" + errorText={this.state.errors.newPassword} + type="password" + value={this.state.newPassword} + fullWidth + /> + this.onInputChange(v, "newPasswordConfirm")} + name="newPasswordConfirm" + floatingLabelText="Confirm New Password" + errorText={this.state.errors.newPasswordConfirm} + type="password" + value={this.state.newPasswordConfirm} + fullWidth + /> + {this.submitButtons("currentPassword")} + +
+ ); + + + return ( +
+ + + {nameField} +
+ {emailField} +
+

Update your password

+ {password} + + + + + + + + A confirmation link has been sent to your new email address. +
+ Your current email will still remain active until the new email is activated. +
+ +
+ ); + } + static propTypes = { + /** User's name */ + name: PropTypes.string, + /** User's email address */ + email: PropTypes.string, + /** Error message for the current password text field */ + currentPasswordError: PropTypes.string, + /** Error message for the email text field */ + emailTakenError: PropTypes.string, + /** True if user profile modal should be opened */ + userProfileModalOpen: PropTypes.bool, + /** Callback to update user account */ + handleUserProfileUpdate: PropTypes.func, + /** Callback to toggle the user profile modal */ + toggleUserProfile: PropTypes.func, + /** Callback to delete user account */ + handleUserAccountDelete: PropTypes.func, + } +} + +export default UserProfileForm; diff --git a/viscoll-app/src/components/topbar/UserProfileForm.md b/viscoll-app/src/components/topbar/UserProfileForm.md new file mode 100644 index 00000000..2dcb2147 --- /dev/null +++ b/viscoll-app/src/components/topbar/UserProfileForm.md @@ -0,0 +1,16 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| name | string | User's name | +| email | string | User's email address | +| emailMessagePending | boolean | Gets set to `true` when user submits a new email address | +| emailMessage | boolean | `true` to show a message that the new email address is pending user activation. This gets toggled only when `emailMessagePending=true` and there are no email errors. | +| currentPassword | string | The current password | +| newPassword | string | New password | +| newPasswordConfirm | string | New password | +| deleteDialog | boolean | `true` to show delete confirmation dialog | +| unsavedDialog | boolean | `true` to show unsaved changes dialog | +| errors | object | Error messages
name: string
email: string
newPassword: string
newPasswordConfirm: string
currentPassword: string| +| editing | object | Track fields are that being edited
name: boolean
email: boolean
newPassword: boolean
newPasswordConfirm: boolean
currentPassword: boolean| + diff --git a/viscoll-app/src/containers/App.js b/viscoll-app/src/containers/App.js new file mode 100644 index 00000000..6c488011 --- /dev/null +++ b/viscoll-app/src/containers/App.js @@ -0,0 +1,66 @@ +import React, { Component } from 'react'; +import injectTapEventPlugin from 'react-tap-event-plugin'; +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; +import getMuiTheme from 'material-ui/styles/getMuiTheme'; +import light from '../styles/light'; +import '../styles/App.css'; +import Authentication from './Authentication'; +import Dashboard from './Dashboard'; +import Project from './Project'; +import {persistStore} from 'redux-persist' +import store from "../store"; +import {Provider} from "react-redux"; +import AppLoadingScreen from "../components/global/AppLoadingScreen"; +import localForage from 'localforage' +import PageNotFound from '../components/global/PageNotFound'; + +import { + BrowserRouter as Router, + Route, + Switch +} from 'react-router-dom' + +injectTapEventPlugin(); + +/** Main app */ +class App extends Component { + + constructor() { + super() + this.state = { rehydrated: false } + } + + componentWillMount(){ + persistStore(store, {storage: localForage}, () => { + setTimeout(()=>{this.setState({ rehydrated: true })}, 500); + }) + } + + render() { + if (!this.state.rehydrated) { + return ( + + + + ) + } + return ( + + + + + + + + + + + + + + + ); + } +} + +export default App; diff --git a/viscoll-app/src/containers/Authentication.js b/viscoll-app/src/containers/Authentication.js new file mode 100644 index 00000000..7b0fa792 --- /dev/null +++ b/viscoll-app/src/containers/Authentication.js @@ -0,0 +1,250 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import RaisedButton from 'material-ui/RaisedButton'; +import imgCollation from '../assets/collation.png'; +import imgLogo from '../assets/logo_white.png'; +import Register from '../components/authentication/Register'; +import Login from '../components/authentication/Login'; +import ResetPassword from '../components/authentication/ResetPassword'; +import ResetPasswordRequest from '../components/authentication/ResetPasswordRequest'; +import ResendConfirmation from '../components/authentication/ResendConfirmation'; +import {btnLg} from '../styles/button'; +import { connect } from "react-redux"; +import { + login, + register, + confirm, + resetPasswordRequest, + resetPassword, + logout, + resendConfirmation, +} from "../actions/userActions"; + + +/** Landing page of the app. Contain register, login and password reset forms. */ +class Landing extends Component { + + constructor(props) { + super(props); + this.state = { + register: false, + login: false, + reset: false, + resetRequest: false, + reset_token: "", + message: "", + resendConfirmation: false, + resendConfirmationSuccess: false, + } + } + /** + * Toggle the `Register` component + * @public + */ + toggleRegister = () => { + this.setState({register: !this.state.register, message: ""}); + } + + /** + * Toggle the `Login` component + * @public + */ + toggleLogin = () => { + this.setState({login: !this.state.login, message: ""}); + } + + /** + * Toggle the `ResetPassword` component + * @public + */ + toggleResetRequest = () => { + this.setState({resetRequest: !this.state.resetRequest, message: ""}); + } + + /** + * Unmount any mounted forms + * @public + */ + tapCancel = () => { + this.setState({login: false, register: false, resetRequest: false, resendConfirmation: false, message: ""}); + } + + /** + * Show message on reset password success + * @public + */ + handleResetPasswordSuccess = () => { + this.setState({reset: false, message: "Your password has been successfully updated. Go ahead and login."}); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.user.errors.confirmation.length>0) { + this.setState({resendConfirmation: true}); + } + if (nextProps.notification.includes("successfully confirmed your account")) { + this.setState({message: nextProps.notification, resendConfirmationSuccess: true}); + } + } + + componentDidMount() { + const token = this.props.location.search.split('=')[1]; + if (token) { + if (this.props.location.pathname.includes("confirmation")){ + this.props.confirmUser(token); + if (this.props.user.authenticated) this.props.logoutUser(); + } else if (this.props.location.pathname.includes("password")) { + this.setState({ reset: true, reset_token: token }); + } + } else { + if (this.props.user.authenticated) { + this.props.history.push('/dashboard'); + } + } + } + + render() { + const message =

{this.state.message}

; + + let resetPassword = ""; + let resetPasswordRequest = ""; + let resendConfirmation = ""; + + let register = ( +
+ +
+ ); + let login = ( +
+ +
+ ); + + if (this.state.register) { + register = ; + login = ""; + } else if (this.state.resetRequest) { + login = ""; + register = ""; + resetPassword = ""; + resetPasswordRequest = + } else if (this.state.login) { + register = ""; + login = ; + } else if (this.state.reset) { + login = ""; + register = ""; + resetPassword = ; + + } else if (this.state.resendConfirmation) { + login = ""; + register = ""; + resendConfirmation = + } else if (this.state.resendConfirmationSuccess) { + register = ""; + } + + return ( +
+
+
+ Logo +
+
+ LogoWhite +
+
+ {message} + {register} + {login} + {resetPasswordRequest} + {resetPassword} + {resendConfirmation} +
+
+
+
+ ); + } + static propTypes = { + /** History object from React Router */ + history: PropTypes.object, + /** Location object from React Router */ + location: PropTypes.object, + /** Match object from React Router */ + match: PropTypes.object, + /** User object from Redux store */ + user: PropTypes.object, + } +} + + +const mapStateToProps = (state) => { + return { + user: state.user, + notification: state.global.notification, + }; +}; + + +const mapDispatchToProps = (dispatch) => { + return { + logoutUser: () => { + dispatch(logout()); + }, + loginUser: (user) => { + dispatch(login(user)); + }, + registerUser: (user) => { + dispatch(register(user)); + }, + confirmUser: (confirmation_token) => { + dispatch(confirm(confirmation_token)); + }, + resetPasswordRequest: (email) => { + dispatch(resetPasswordRequest(email)); + }, + resetPassword: (reset_token, password) => { + dispatch(resetPassword(reset_token, password)); + }, + resendConfirmation: (email) => { + dispatch(resendConfirmation(email)); + }, + }; +}; + + +export default connect(mapStateToProps, mapDispatchToProps)(Landing); diff --git a/viscoll-app/src/containers/Authentication.md b/viscoll-app/src/containers/Authentication.md new file mode 100644 index 00000000..e4e8690f --- /dev/null +++ b/viscoll-app/src/containers/Authentication.md @@ -0,0 +1,13 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| register | boolean | `true` to mount the `Register` component | +| login | boolean | `true` to mount the `Login` component | +| reset | boolean | `true` to mount the `ResetPassword` component | +| resetRequest | boolean | `true` to mount the `ResetPasswordRequest` component | +| reset_token | string | Reset password token | +| message | string | Message to display above the register/login/reset forms | +| confirmed | boolean | NOT SURE IF NEED THIS??? | +| confirmation_token | string | NOT SURE IF NEED THIS??? | + diff --git a/viscoll-app/src/containers/CollationManager.js b/viscoll-app/src/containers/CollationManager.js new file mode 100644 index 00000000..5bde092d --- /dev/null +++ b/viscoll-app/src/containers/CollationManager.js @@ -0,0 +1,558 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {Tabs, Tab} from 'material-ui/Tabs'; +import FlatButton from 'material-ui/FlatButton'; +import TabularMode from '../components/collationManager/TabularMode'; +import VisualMode from '../components/collationManager/VisualMode'; +import ViewingMode from '../components/collationManager/ViewingMode'; +import Panel from '../components/global/Panel'; +import Export from '../components/export/Export'; +import InfoBox from './InfoBox'; +import Filter from './Filter'; +import TopBar from "./TopBar"; +import topbarStyle from "../styles/topbar"; +import IconClear from 'material-ui/svg-icons/content/clear'; +import IconButton from 'material-ui/IconButton'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; +import { connect } from "react-redux"; +import { changeViewMode, + handleObjectClick, + changeManagerMode, + toggleFilterPanel, + updateFilterSelection, + toggleTacket, +} from "../actions/editCollation/interactionActions"; +import { + loadProject, + updateGroup, +} from "../actions/editCollation/modificationActions"; +import { exportProject } from "../actions/projectActions"; +import { updateProject } from "../actions/projectActions"; +import fileDownload from 'js-file-download'; + + +/** Container for `TabularMode`, `VisualMode`, `InfoBox`, `TopBar`, `LoadingScreen`, and `Notification`. This container has the project sidebar embedded directly. */ +class CollationManager extends Component { + + constructor(props) { + super(props); + this.state = { + contentStyle: { + transition: 'top 450ms cubic-bezier(0.23, 1, 0.32, 1)', + top: 60, + }, + infoboxStyle: { + maxHeight: "90%" + }, + export: { + open: false, + label: "", + type: "" + }, + selectAll: "", + leftSideBarOpen: true, + showTips: props.preferences.showTips + }; + } + + componentWillMount() { + if (this.props.collationManager.viewMode==="VIEWING") { + this.setState({leftSideBarOpen:false}); + } + } + + componentDidMount() { + if (this.props.filterPanelOpen){ + let filterContainer = document.getElementById('filterContainer'); + if (filterContainer) { + let filterPanelHeight = filterContainer.offsetHeight; + this.filterHeightChange(filterPanelHeight); + } + } + } + + componentWillReceiveProps(nextProps) { + const newSelectedObjects = nextProps.selectedObjects!==this.props.selectedObjects; + const differentNumOfSelectedObjects = this.state.selectAll && nextProps.selectedObjects.members.length!==Object.keys(this.props.project[this.state.selectAll]).length; + const differentTypeOfSelectedObjects = this.state.selectAll!==(nextProps.selectedObjects.type+"s"); + if (this.state.selectAll && newSelectedObjects && (differentTypeOfSelectedObjects || differentNumOfSelectedObjects)) { + this.setState({selectAll:""}); + } + if (nextProps.selectedObjects.type && Object.keys(nextProps.project[nextProps.selectedObjects.type+"s"]).length===nextProps.selectedObjects.members.length) { + this.setState({selectAll:nextProps.selectedObjects.type+"s"}); + } + } + + + /** + * Toggle filter panel + * @public + */ + toggleFilterDrawer = () => { + this.props.toggleFilterPanel(!this.props.filterPanelOpen); + let filterPanelHeight = document.getElementById('filterContainer').offsetHeight; + if (this.props.filterPanelOpen) { + filterPanelHeight = 0; + } + this.filterHeightChange(filterPanelHeight); + } + + + /** + * Pass the newly clicked object to the `handleObjectClick` action + * @param {object} object + * @param {object} event + * @public + */ + handleObjectClick = (object, event) => { + this.props.handleObjectClick( + this.props.selectedObjects, + object, + event, + this.props.project.Groups, + this.props.project.Leafs, + this.props.project.Rectos, + this.props.project.Versos, + ); + } + /** + * Pass new view mode value (`VISUAL`, `TABULAR` or `VIEWING`) to the `changeViewMode` action + * @param {string} value + * @public + */ + handleViewModeChange = (value) => { + if (value==="VIEWING" && this.state.leftSideBarOpen) { + this.setState({leftSideBarOpen: false}, ()=>this.props.changeViewMode(value)); + } else if (value!=="VIEWING" && this.state.leftSideBarOpen===false) { + this.setState({leftSideBarOpen: true}, ()=>this.props.changeViewMode(value)); + } else { + this.props.changeViewMode(value); + } + } + + /** + * Update the content style when filter panel height changes + * @param {number} value new height + * @public + */ + filterHeightChange = (value) => { + let infoboxHeight = "90%"; + if (value>0) infoboxHeight = window.innerHeight - value - 56 - 30 + "px"; + this.setState({ + contentStyle:{...this.state.contentStyle, top:value+56}, + infoBoxStyle: {maxHeight: infoboxHeight}, + }); + } + + /** + * Submit update group request + * @param {string} groupID + * @param {object} group + * @public + */ + updateGroup = (groupID, group) => { this.props.updateGroup(groupID, group, this.props); } + + + closeTip = () => { + const project = { + preferences: { + showTips: false + } + }; + this.props.updateProject(project, this.props.project.id) + } + + handleSelection = (selection) => { + this.props.updateFilterSelection( + selection, + this.props.project.Groups, + this.props.project.Leafs, + this.props.project.Rectos, + this.props.project.Versos + ); + } + + + handleExportToggle = (open, type, label) => { + this.setState({export: {open, type, label}}, ()=>{ + if (this.state.export.open) + this.props.exportProject(this.props.project.id, type); + }); + }; + + + showCopyToClipboardNotification = () => { + this.props.showCopyToClipboardNotification(); + } + + handleDownloadCollationDiagram = () => { + let canvas = document.getElementById("myCanvas"); + canvas.toBlob((blob)=>{ + const filename = this.props.project.title.replace(/\s/g, "_"); + fileDownload(blob, `${filename}.PNG`) + }); + + } + + + render() { + const containerStyle = {top: 85, right: "2%", height: 'inherit', maxHeight: '80%', width: '28%'}; + if (!this.state.leftSideBarOpen) { + containerStyle["width"] = "30%"; + } + + const topbar = ( + + + + + + + + ); + + const singleEditTip = 'Hold the CTRL key (or Command key for Mac users) to select multiple groups/leaves/sides. Hold SHIFT key to select a range of groups/leaves/sides.'; + const batchEditTip = 'You are in batch edit mode. To leave this mode, click on any group/leaf/side without holding down any keys.'; + const tip = this.props.selectedObjects.members.length>1 ? batchEditTip : singleEditTip + let tipsDiv; + if (this.props.managerMode==="collationManager" && this.props.preferences.showTips===true) { + tipsDiv = +
+
+ + + +
+
+ TIP: {tip} +
+
+ } + + const selectionRadioGroup = ( + this.setState({selectAll: v}, ()=>{this.handleSelection(v+"_all")})} + > + + + + + + ); + + const exportDialog = ( + + ); + + const sidebar = ( +
+
+ {tipsDiv} + +
this.props.changeManagerMode("collationManager")} > + Collation +
+
this.props.changeManagerMode("notesManager")} > + Notes +
+
this.props.changeManagerMode("imageManager")} > + Images +
+
+ + {selectionRadioGroup} + this.setState({selectAll:""},this.handleSelection(""))} + secondary + fullWidth + style={this.state.selectAll===""?{display:"none"}:{}} + /> + + +

Export Collation Data

+
+
+ this.handleExportToggle(true, "json", "JSON")} + /> +
+
+ this.handleExportToggle(true, "xml", "XML")} + /> +
+
+ this.handleExportToggle(true, "formula", "Collation Formula")} + /> +
+
+

Export Collation Diagram

+
+
+ +
+
+
+ +
+ ); + + const infobox = ( +
+ +
+ ) + + let workspace =
; + if (this.props.project.groupIDs.length>0){ + if (this.props.collationManager.viewMode==="TABULAR") { + workspace = ( +
+
+ +
+ {infobox} + {exportDialog} +
+ ); + } else if (this.props.collationManager.viewMode==="VISUAL") { + workspace = ( +
+
+ +
+ {infobox} + {exportDialog} +
+ ); + } else { + workspace = ( +
+
+ +
+ {infobox} + {exportDialog} +
+ ); + } + } + if (this.props.project.groupIDs.length===0 && !this.props.loading){ + workspace = ( +
+
+

+ It looks like you have an empty project. Add a new Group to start collating. +

+
+ {infobox} +
+ ); + } + return ( +
+ {topbar} + {sidebar} + + {workspace} +
+ ); + } + + static propTypes = { + /** History object from React Router */ + history: PropTypes.object, + /** Location object from React Router */ + location: PropTypes.object, + /** Match object from React Router */ + match: PropTypes.object, + /** User object from Redux store */ + user: PropTypes.object, + /** Project that is being edited */ + project: PropTypes.object, + /** Boolean if loading screen should appear - from Redux store */ + loading: PropTypes.bool, + /** Dictionary containing arrays of updated leaf/group ID's to 'flash' - from Redux store */ + flashItems: PropTypes.shape({ + leaves: PropTypes.arrayOf(PropTypes.number), + groups: PropTypes.arrayOf(PropTypes.number) + }), + } +} + + +const mapStateToProps = (state) => { + return { + user: state.user, + project: state.active.project, + preferences: state.active.project.preferences, + managerMode: state.active.managerMode, + filterPanelOpen: state.active.collationManager.filters.filterPanelOpen, + selectedObjects: state.active.collationManager.selectedObjects, + collationManager: state.active.collationManager, + loading: state.global.loading, + exportedData: state.active.exportedData, + tacketing: state.active.collationManager.visualizations.tacketing, + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + toggleTacket: (toggle) => { + dispatch(toggleTacket(toggle)); + }, + + loadProject: (projectID, props) => { + dispatch(loadProject(projectID)); + }, + + changeViewMode: (viewMode) => { + dispatch(changeViewMode(viewMode)); + }, + + handleObjectClick: (selectedObjects, object, event, Groups, Leafs, Rectos, Versos) => { + dispatch(handleObjectClick(selectedObjects, object, event, {Groups, Leafs, Rectos, Versos})); + }, + + changeManagerMode: (managerMode) => { + dispatch(changeManagerMode(managerMode)); + }, + + toggleFilterPanel: (value) => { + dispatch(toggleFilterPanel(value)); + }, + + updateProject: (project, projectID) => { + dispatch(updateProject(projectID, project)); + }, + + updateGroup: (groupID, group, props) => { + dispatch(updateGroup(groupID, group)); + }, + + updateFilterSelection: ( + selection, + Groups, + Leafs, + Rectos, + Versos + ) => { + dispatch(updateFilterSelection( + selection, + [], + {Groups, Leafs, Rectos, Versos} + )); + }, + + exportProject: (projectID, format) => { + dispatch(exportProject(projectID, format)); + }, + + showCopyToClipboardNotification: () => { + dispatch({ + type: "SHOW_NOTIFICATION", + payload: "Successfully copied to clipboard" + }); + setTimeout(()=>dispatch({type: "HIDE_NOTIFICATION"}), 4000); + } + }; +}; + + +export default connect(mapStateToProps, mapDispatchToProps)(CollationManager); + diff --git a/viscoll-app/src/containers/Dashboard.js b/viscoll-app/src/containers/Dashboard.js new file mode 100644 index 00000000..1a4ce6f3 --- /dev/null +++ b/viscoll-app/src/containers/Dashboard.js @@ -0,0 +1,224 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import Drawer from 'material-ui/Drawer'; +import RaisedButton from 'material-ui/RaisedButton'; +import NewProjectContainer from '../components/dashboard/NewProjectContainer'; +import EditProjectForm from '../components/dashboard/EditProjectForm'; +import ListView from '../components/dashboard/ListView'; +import LoadingScreen from "../components/global/LoadingScreen"; +import Notification from "../components/global/Notification"; +import TopBar from "./TopBar"; +import Feedback from "./Feedback"; +import {Tabs,Tab} from 'material-ui/Tabs'; +import topbarStyle from "../styles/topbar"; +import {btnMd} from '../styles/button'; +import { connect } from "react-redux"; +import { + createProject, + updateProject, + deleteProject, + loadProjects, + importProject, + cloneProjectExport, + cloneProjectImport +} from "../actions/projectActions"; + +/** Dashboard where user is directed to upon login. This is where the user an create a new project or edit an existing project. */ +class Dashboard extends Component { + constructor(props) { + super(props); + this.state = { + newProjectPopoverOpen: false, + newProjectModalOpen: false, + selectedProjectIndex: -1, + selectedProject: {}, + projectDrawerOpen: false, + }; + } + + componentWillMount() { + this.props.user.authenticated ? this.props.loadProjects(this.props) : this.props.history.push('/'); + } + + componentDidUpdate() { + if (!this.props.user.authenticated) this.props.history.push('/'); + } + + closeProjectPanel = () => { + this.setState({projectDrawerOpen: false, selectedProjectIndex: -1, selectedProject: {}}); + } + + doubleClick = projectID => this.props.history.push(`/project/${projectID}`); + + handleProjectSelection = (index) => { + if (index>=0) { + let project = this.state.selectedProject; + let toggle = this.state.projectDrawerOpen; + project = this.props.projects[index]; + if (!Object.keys(this.state.selectedProject).length>0) {toggle = !toggle}; + this.setState({projectDrawerOpen: toggle, selectedProject: project, selectedProjectIndex: index}); + } + } + + handleNewProjectTouchTap = (event) => { + event.preventDefault(); + this.setState({newProjectPopoverOpen: true, popoverAnchorEl: event.currentTarget}); + }; + + handleNewProjectRequestClose = () => this.setState({ newProjectPopoverOpen: false }); + + handleNewProjectModalToggle = (value) => { + this.setState({newProjectModalOpen: value, newProjectPopoverOpen: false}); + } + + handleImportProject = (data) => { + this.props.importProject(data) + .then((action)=>{ + if (action.type==="IMPORT_PROJECT_SUCCESS"){ + this.handleProjectSelection(0); + this.props.importProjectCallback(); + } + }); + } + + handleCloneProject = (projectID) => { + this.props.cloneProjectExport(projectID) + .then((action)=>{ + if (action.type==="CLONE_PROJECT_EXPORT_SUCCESS"){ + const importData = JSON.stringify(action.payload, null, 4); + this.props.cloneProjectImport({importData, importFormat: "json"}) + .then((action)=>{ + if (action.type==="CLONE_PROJECT_IMPORT_SUCCESS"){ + this.handleProjectSelection(0); + this.props.importProjectCallback(); + } + }) + } + }); + } + + render() { + let sidebar = ( +
+
+
+ this.handleNewProjectModalToggle(true)} + /> +
+ ); + let projectPane = ( + + + + ); + + return ( +
+ + + + + + {sidebar} + {projectPane} + this.handleNewProjectModalToggle(false)} + user={this.props.user} + createProject={this.props.createProject} + importProject={this.handleImportProject} + importStatus={this.props.importStatus} + cloneProject={this.handleCloneProject} + /> +
+ +
+ + + +
+ ); + } + static propTypes = { + /** History object from React Router */ + history: PropTypes.object, + /** User object from Redux store */ + user: PropTypes.object, + /** Array of project objects from Redux store */ + projects: PropTypes.arrayOf(PropTypes.object), + /** Boolean if loading screen should appear - from Redux store */ + loading: PropTypes.bool, + /** Notification message from Redux store */ + notification: PropTypes.string, + } +} + +const mapStateToProps = (state) => { + return { + user: state.user, + projects: state.projects.projects, + importStatus: state.projects.importStatus, + loading: state.global.loading, + notification: state.global.notification + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + createProject: (newProject) => { + dispatch(createProject(newProject)); + }, + updateProject: (projectID, project) => { + dispatch(updateProject(projectID, project)) + }, + deleteProject: (projectID) => { + dispatch(deleteProject(projectID)); + }, + loadProjects: (props) => { + dispatch(loadProjects()); + }, + importProject: (data) => { + return dispatch(importProject(data)) + }, + importProjectCallback: () => { + dispatch({type: "IMPORT_PROJECT_SUCCESS_CALLBACK"}); + }, + cloneProjectExport: (projectID) => { + return dispatch(cloneProjectExport(projectID)); + }, + cloneProjectImport: (data) => { + return dispatch(cloneProjectImport(data)); + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); + + diff --git a/viscoll-app/src/containers/Dashboard.md b/viscoll-app/src/containers/Dashboard.md new file mode 100644 index 00000000..dacdbfa1 --- /dev/null +++ b/viscoll-app/src/containers/Dashboard.md @@ -0,0 +1,9 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| newProjectPopoverOpen | boolean | `true` to show the `New` button popover menu | +| newProjectModalOpen | boolean | `true` to show the new project modal | +| projectDrawerOpen | boolean | `true` to show the project info panel | +| selectedProjectIndex | number | index of selected project from the list of user's projects | +| selectedProject | object | Selected project object | diff --git a/viscoll-app/src/containers/Feedback.js b/viscoll-app/src/containers/Feedback.js new file mode 100644 index 00000000..dea829b4 --- /dev/null +++ b/viscoll-app/src/containers/Feedback.js @@ -0,0 +1,133 @@ +import React, {Component} from 'react'; +import { connect } from "react-redux"; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import TextField from 'material-ui/TextField'; +import ClientJS from 'clientjs'; +import { exportProjectBeforeFeedback } from "../actions/projectActions"; +import { sendFeedback } from "../actions/userActions"; + +/** Feedback form that submits a JIRA ticket for each feedback */ +class Feedback extends Component { + constructor(props) { + super(props); + this.state = { + open: false, + title: "", + feedback: "", + } + } + handleOpen = () => { + this.setState({open: true}); + } + handleClose = () => { + this.setState({ + open: false, + title: "", + feedback: "", + }); + } + onChange = (type, value) => { + this.setState({[type]:value}); + } + submit = () => { + let feedback = "Feedback Message:\n" + this.state.feedback + "\n\n"; + try{ + const client = new ClientJS(); + const result = client.getResult(); + feedback = feedback + "Browser Information:\n" + JSON.stringify(result); + } catch (e){} + this.props.sendFeedback(this.state.title, feedback, this.props.userID, this.props.projectID); + this.handleClose(); + } + render() { + const actions = [ + , + , + ]; + return ( +
+
+ +
+ +

Bug? Suggestions? Let us know!

+
+
+

Title

+
+
+ this.onChange("title", v)} + /> +
+
+
+
+

Feedback

+
+
+ this.onChange("feedback", v)} + /> +
+
+
+
+ ); + } +} +const mapStateToProps = (state) => { + return { + userID: state.user.id, + projectID: state.active.project.id + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + sendFeedback: (title, message, userID, projectID) => { + if (projectID){ + dispatch(exportProjectBeforeFeedback(projectID, "json")) + .then((action)=>{ + if (action.type==="EXPORT_SUCCESS"){ + message = message + "\n\nProject Information:\n" + JSON.stringify(action.payload); + dispatch(sendFeedback(title, message, userID)); + } + }) + } else { + dispatch(sendFeedback(title, message, userID)); + } + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(Feedback); diff --git a/viscoll-app/src/containers/Filter.js b/viscoll-app/src/containers/Filter.js new file mode 100644 index 00000000..074fcdcc --- /dev/null +++ b/viscoll-app/src/containers/Filter.js @@ -0,0 +1,468 @@ +import React, {Component} from 'react'; +import { connect } from "react-redux"; +import FilterRow from '../components/filter/FilterRow'; +import RaisedButton from 'material-ui/RaisedButton'; +import MenuItem from 'material-ui/MenuItem'; +import Toggle from 'material-ui/Toggle'; +import Popover, {PopoverAnimationVertical} from 'material-ui/Popover'; +import Menu from 'material-ui/Menu'; +import ArrowDown from 'material-ui/svg-icons/navigation/arrow-drop-down' +import { + filterProject, + resetFilters, + toggleFilterDisplay, + updateFilterQuery, + updateFilterSelection +} from "../actions/editCollation/interactionActions"; + +/** Filter groups, leaves and sides */ +class Filter extends Component { + + constructor(props) { + super(props); + this.state = { + queries: props.queries, + submitDisabled: false, + conjoinedToAutoComplete: [], + select: "", + popoverAnchorEl: null, + selectPopover: false, + message: "", + filterPanelHeight: '0', + } + } + + componentDidMount() { + let conjoinedToAutoComplete = []; + for (let id in this.props.attachedToLeafs){ + conjoinedToAutoComplete.push({ + textKey: `Leaf ${id}`, + valueKey: this.props.attachedToLeafs[id] + }) + } + if (this.props.Leafs){ + for (let leafID in this.props.Leafs){ + const leaf = this.props.Leafs[leafID]; + conjoinedToAutoComplete.push({ + textKey: `Leaf ${leaf.order}`, + valueKey: leaf.id + }) + } + } + this.setState({ + conjoinedToAutoComplete, + filterPanelHeight: document.getElementById('filterContainer').offsetHeight + }); + } + + componentWillReceiveProps(nextProps) { + let conjoinedToAutoComplete = []; + for (let id in nextProps.attachedToLeafs){ + conjoinedToAutoComplete.push({ + textKey: `Leaf ${id}`, + valueKey: nextProps.attachedToLeafs[id] + }) + } + if (nextProps.Leafs){ + for (let leafID in nextProps.Leafs){ + const leaf = nextProps.Leafs[leafID]; + conjoinedToAutoComplete.push({ + textKey: `Leaf ${leaf.order}`, + valueKey: leaf.id + }) + } + } + let matches = []; + if (nextProps.groupMatches.length>0) { + let plural = nextProps.groupMatches.length>1? "s" : ""; + matches.push(nextProps.groupMatches.length + " group" + plural); + } + if (nextProps.leafMatches.length>0) { + let plural = nextProps.leafMatches.length>1? "ves" : "f"; + matches.push(nextProps.leafMatches.length + " lea" + plural); + } + if (nextProps.sideMatches.length>0) { + let plural = nextProps.sideMatches.length>1? "s" : ""; + matches.push(nextProps.sideMatches.length + " side" + plural); + } + if (nextProps.noteMatches.length>0) { + let plural = nextProps.noteMatches.length>1? "s" : ""; + matches.push(nextProps.noteMatches.length + " note" + plural); + } + let message = "No matches found."; + if (matches.length>0) { + message = "Matches: " + matches.join(", "); + } + if (nextProps.queries.length===1 && + (nextProps.queries[0].type===null || nextProps.queries[0].attribute==="" + || nextProps.queries[0].condition===""||nextProps.queries[0].values.length===0)) { + // Set message to empty if user cleared all filters + message = ""; + } + + let filter = (this.props.open===nextProps.open && + nextProps.hideOthers===this.props.hideOthers && + nextProps.filterSelection===this.props.filterSelection && + nextProps.selectedObjects.members===this.props.selectedObjects.members && + (this.props.groupMatches===nextProps.groupMatches || this.props.leafMatches===nextProps.leafMatches + || this.props.sideMatches===nextProps.sideMatches || this.props.noteMatches===nextProps.noteMatches)); + + let filterPanelHeight = document.getElementById('filterContainer').offsetHeight; + this.setState({ + queries: nextProps.queries, + conjoinedToAutoComplete, + message, filterPanelHeight + }, ()=>{ + if (filter) this.filter(this.state.queries); + }); + + } + + removeRow = (queryIndex) => { + let newQueries = this.state.queries; + newQueries.splice(queryIndex,1); + this.setState({ queries: newQueries}, ()=>{ + this.filter(); + this.props.filterHeightChange(document.getElementById('filterContainer').offsetHeight); + }); + } + + addRow = () => { + if (!this.disableAddRow()) { + let newQueries = this.state.queries; + newQueries.push({ + type: null, + attribute: "", + attributeIndex: "", + values: [], + condition: "", + conjunction: "", + }) + this.setState({ queries: newQueries}, () => { + this.filter(); + this.props.filterHeightChange(document.getElementById('filterContainer').offsetHeight); + }); + + } + } + + onChange = (queryIndex, fieldName, event, index, value, dataSource) => { + if (dataSource){ + for (let member of dataSource){ + if (member.textKey===value){ + value = [member.valueKey]; + break; + } + } + } + let updatedQueries = this.state.queries; + if (["group", "leaf", "side", "note"].includes(value)) + updatedQueries = this.clearFilterRowOnType(queryIndex, value); + this.props.updateFilterQuery(updatedQueries, queryIndex, fieldName, index, value); + } + + filterProject = () => { + // Check if correct values are being passed in auto-complete dropdown cases + let toFilter = true; + for (let query of this.state.queries){ + if (query.type==="leaf" && query.attribute==="conjoined_leaf_order"){ + if (!Array.isArray(query.values)){ + toFilter = false; + break; + } + } + else if (query.type==="note" && query.attribute==="type"){ + if (!Array.isArray(query.values)){ + toFilter = false; + break; + } + } + } + if (toFilter) + this.props.filterProject(this.props.projectID, {queries: this.state.queries}); + } + + resetFilters = () => { + this.setState({ + queries: [ + { + type: null, + attribute: "", + attributeIndex: "", + values: [], + condition: "", + conjunction: "", + } + ], + }, () => { + this.props.resetFilters(this.state.queries); + this.props.filterHeightChange(document.getElementById('filterContainer').offsetHeight); + }); + } + + clearFilterRowOnType = (index, type) => { + let queries = []; + let currentIndex = 0; + for (let query of this.state.queries) { + if (currentIndex===index && type!==query.type) + queries.push({ + type: type, + attribute: "", + attributeIndex: query.attributeIndex, + values: [], + condition: "", + conjunction: "", + }) + else + queries.push(query); + currentIndex += 1; + } + return queries; + } + + clearFilterRowOnAttribute = (index, attribute, attributeIndex) => { + let queries = []; + let currentIndex = 0; + for (let query of this.state.queries) { + if (currentIndex===index && attribute!==query.attribute && query.attribute!==""){ + queries.push({ + type: query.type, + attribute: attribute, + attributeIndex: attributeIndex, + values: [], + condition: "", + conjunction: "", + }) + } + else + queries.push(query); + currentIndex += 1; + } + this.setState({queries}, () => this.props.resetFilters(queries)) + } + + + filter = () => { + let index = 0; + let haveErrors = false + for (let query of this.state.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 !== this.state.queries.length-1) + if (query.conjunction === "") + haveErrors = true + index += 1; + } + if (!haveErrors) this.filterProject(); + } + + disableAddRow = () => { + if (this.state.queries[this.state.queries.length-1].type === null) + return true; + if (this.state.queries[this.state.queries.length-1].attribute === "") + return true; + if (this.state.queries[this.state.queries.length-1].values.length === 0) + return true; + if (this.state.queries[this.state.queries.length-1].condition === "") + return true; + return false; + } + + disableNewRow = () => { + return (this.state.queries.length>1 && this.state.queries[this.state.queries.length-2].conjunction === ""); + } + + + handleSelection = (selection) => { + if (this.props.filterSelection!==selection || selection==="") + this.props.updateFilterSelection( + selection, + this.props.matchingFilterObjects, + this.props.Groups, + this.props.Leafs, + this.props.Rectos, + this.props.Versos + ); + } + + handleSelectOpen = (event) => { + // This prevents ghost click. + event.preventDefault(); + if (this.props.filterActive) { + this.setState({ + selectPopover: true, + popoverAnchorEl: event.currentTarget, + }); + } + } + + handleSelectClose = () => { + this.setState({ + selectPopover: false, + }); + }; + + render() { + let queries = []; + if (this.state.queries) + for (let i=0; i + ); + } + let filterContainerStyle = this.props.open?{top:'56px'}:{top:'-'+this.state.filterPanelHeight+'px'}; + if (this.props.fullWidth) filterContainerStyle.width="100%"; + let panel = +
+
+ {queries} +
+
+

{this.state.message}

+
+
+
+ +
+
+ } + /> + + {this.setState({select:v});this.handleSelection(v)}} + > + + + + + {this.props.selectedObjects.members.length > 0 ? + + : null + } + + +
+
+ this.resetFilters()} + style={{marginRight: 15}} + backgroundColor="#D87979" + labelColor="#ffffff" + /> +
+
+
+
+
+ + return panel; + } +} + +const mapStateToProps = (state) => { + return { + projectID: state.active.project.id, + selectedObjects: state.active.collationManager.selectedObjects, + viewMode: state.active.collationManager.viewMode, + defaultAttributes: state.active.collationManager.defaultAttributes, + Groups: state.active.project.Groups, + Leafs: state.active.project.Leafs, + Rectos: state.active.project.Rectos, + Versos: state.active.project.Versos, + Notes: state.active.project.Notes, + attachedToLeafs: state.active.project.attachedToLeafs, + queries: state.active.collationManager.filters.queries, + hideOthers: state.active.collationManager.filters.hideOthers, + filterActive: state.active.collationManager.filters.active, + filterSelection: state.active.collationManager.filters.selection, + noteTypes: state.active.project.noteTypes, + groupMatches: state.active.collationManager.filters.Groups, + leafMatches: state.active.collationManager.filters.Leafs, + sideMatches: state.active.collationManager.filters.Sides, + noteMatches: state.active.collationManager.filters.Notes, + matchingFilterObjects: state.active.collationManager.filters, + }; +}; +const mapDispatchToProps = (dispatch) => { + return { + filterProject: (projectID, queries) => { + dispatch(filterProject(projectID, queries)); + }, + resetFilters: (queries) => { + dispatch(resetFilters(queries)); + }, + toggleFilterDisplay: () => { + dispatch(toggleFilterDisplay()); + }, + updateFilterQuery: (currentQueries, queryIndex, fieldName, index, value) => { + dispatch(updateFilterQuery(currentQueries, queryIndex, fieldName, index, value)); + }, + updateFilterSelection: ( + selection, + matchingFilterObjects, + Groups, + Leafs, + Rectos, + Versos + ) => { + dispatch(updateFilterSelection( + selection, + matchingFilterObjects, + {Groups, Leafs, Rectos, Versos} + )); + } + }; +}; +export default connect(mapStateToProps, mapDispatchToProps)(Filter); diff --git a/viscoll-app/src/containers/ImageManager.js b/viscoll-app/src/containers/ImageManager.js new file mode 100644 index 00000000..ecd2f5c1 --- /dev/null +++ b/viscoll-app/src/containers/ImageManager.js @@ -0,0 +1,149 @@ +import React, {Component} from 'react'; +import { connect } from "react-redux"; +import TopBar from "./TopBar"; +import {Tabs, Tab} from 'material-ui/Tabs'; +import topbarStyle from "../styles/topbar"; +import Panel from '../components/global/Panel'; +import { + changeManagerMode, + changeImageTab, +} from "../actions/editCollation/interactionActions"; +import { + createManifest, + updateManifest, + deleteManifest, + cancelCreateManifest +} from "../actions/projectActions"; +import { mapSidesToImages } from "../actions/editCollation/modificationActions"; +import { sendFeedback } from "../actions/userActions"; +import ManageManifests from '../components/imageManager/ManageManifests'; +import MapImages from '../components/imageManager/MapImages'; + +class ImageManager extends Component { + + createManifest = (manifest) => { + this.props.createManifest(this.props.projectID, manifest) + } + + updateManifest = (manifest) => { + this.props.updateManifest(this.props.projectID, manifest) + } + + deleteManifest = (manifest) => { + this.props.deleteManifest(this.props.projectID, manifest) + } + + render() { + let content = ""; + if (this.props.activeTab==="MANAGE") { + content = ( + + ) + } else { + content = ( + + ) + } + + const sidebar = ( +
+
+ +
this.props.changeManagerMode("collationManager")} > + Collation +
+
this.props.changeManagerMode("notesManager")} > + Notes +
+
this.props.changeManagerMode("imageManager")} > + Images +
+
+
+ ); + + return
+
+ + this.props.changeImageTab(v)} + > + + + + + {sidebar} +
+ {content} +
+
+
+ } +} + +const mapStateToProps = (state) => { + return { + projectID: state.active.project.id, + manifests: state.active.project.manifests, + Leafs: state.active.project.Leafs, + Rectos: state.active.project.Rectos, + Versos: state.active.project.Versos, + activeTab: state.active.imageManager.activeTab, + managerMode: state.active.managerMode, + createManifestError: state.active.imageManager.manageSources.error + }; +}; +const mapDispatchToProps = (dispatch) => { + return { + changeManagerMode: (managerMode) => { + dispatch(changeManagerMode(managerMode)); + }, + changeImageTab: (tabName) => { + dispatch(changeImageTab(tabName)); + }, + sendFeedback: (title, message) => { + dispatch(sendFeedback(title, message)) + }, + createManifest: (projectID, manifest) => { + dispatch(createManifest(projectID, manifest)) + }, + updateManifest: (projectID, manifest) => { + dispatch(updateManifest(projectID, manifest)) + }, + deleteManifest: (projectID, manifest) => { + dispatch(deleteManifest(projectID, manifest)) + }, + cancelCreateManifest: () => { + dispatch(cancelCreateManifest()) + }, + mapSidesToImages: (linkedSideIDs, images, unlinkedSideIDs) => { + dispatch(mapSidesToImages(linkedSideIDs, images, unlinkedSideIDs)) + }, + }; +}; +export default connect(mapStateToProps, mapDispatchToProps)(ImageManager); diff --git a/viscoll-app/src/containers/InfoBox.js b/viscoll-app/src/containers/InfoBox.js new file mode 100644 index 00000000..5b9d8a17 --- /dev/null +++ b/viscoll-app/src/containers/InfoBox.js @@ -0,0 +1,612 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import GroupInfoBox from '../components/infoBox/GroupInfoBox'; +import LeafInfoBox from '../components/infoBox/LeafInfoBox'; +import SideInfoBox from '../components/infoBox/SideInfoBox'; +import infoBoxStyle from '../styles/infobox'; +import {Tabs, Tab} from 'material-ui/Tabs'; +import RaisedButton from 'material-ui/RaisedButton'; +import AddGroupDialog from '../components/infoBox/dialog/AddGroupDialog'; +import { connect } from "react-redux"; +import { + addLeafs, + updateLeaf, + updateLeafs, + conjoinLeafs, + deleteLeaf, + deleteLeafs, + addGroups, + updateGroup, + updateGroups, + deleteGroup, + deleteGroups, + updateSide, + updateSides, + addNote, + updateNote, + deleteNote, + linkNote, + unlinkNote, +} from '../actions/editCollation/modificationActions'; +import { + toggleVisibility, + flashLeaves, + flashGroups, + changeInfoBoxTab, + reapplyFilterProject, + toggleTacket, +} from '../actions/editCollation/interactionActions'; + + +/** Container of the leaf, group and side infoboxes */ +class InfoBox extends React.Component { + constructor(props) { + super(props); + this.state = { + addGroupDialogOpen: false + } + } + + /** + * Toggle the add group dialog + * @param {boolean} value + * @public + */ + toggleAddGroupDialog = (value=false) => { + this.setState({ addGroupDialogOpen: value }) + } + + /** + * Submit add leaf request + * @param {object} data + * @public + */ + addLeafs = (data) => { this.props.addLeafs(data.leaf, data.additional, this.props.projectID, this.props.filters); } + + /** + * Submit update leaf request + * @param {string} leafID + * @param {object} leaf + * @public + */ + updateLeaf = (leafID, leaf) => { this.props.updateLeaf(leafID, leaf, this.props.projectID, this.props.filters); } + + /** + * Submit update multiple leaves request + * @param {object} leafs + * @public + */ + updateLeafs = (leafs) => { this.props.updateLeafs(leafs, this.props.projectID, this.props.filters); } + + /** + * Submit conjoin leaves request + * @public + */ + conjoinLeafs = () => { + this.props.conjoinLeafs(this.props.selectedObjects.members, this.props.projectID, this.props.filters); } + + /** + * Submit delete leaf request + * @param {string} leafID + * @public + */ + deleteLeaf = (leafID) => { this.props.deleteLeaf(leafID, this.props.projectID, this.props.filters); } + + /** + * Submit delete multiple leaves request + * @param {object} leafs + * @public + */ + deleteLeafs = (leafs) => { this.props.deleteLeafs(leafs, this.props.projectID, this.props.filters); } + + /** + * Submit update group request + * @param {string} groupID + * @param {object} group + * @public + */ + updateGroup = (groupID, group) => { this.props.updateGroup(groupID, group, this.props.projectID, this.props.filters); } + + /** + * Submit update multiple groups request + * @param {object} data + * @public + */ + updateGroups = (groups) => { this.props.updateGroups(groups, this.props.projectID, this.props.filters); } + + /** + * Submit add multiple groups request + * @param {object} data + * @public + */ + addGroups = (data) => { this.props.addGroups(data.group, data.additional, this.props.projectID, this.props.filters); } + + /** + * Submit delete group request + * @param {string} groupID + * @public + */ + deleteGroup = (groupID) => { this.props.deleteGroup(groupID, this.props.projectID, this.props.filters); } + + /** + * Submit delete multiple groups request + * @param {object} groups + * @public + */ + deleteGroups = (groups) => { this.props.deleteGroups(groups, this.props.projectID, this.props.filters); } + + /** + * Submit update side request + * @param {string} sideID + * @param {object} side + * @public + */ + updateSide = (sideID, side) => { this.props.updateSide(sideID, side, this.props.projectID, this.props.filters); } + + /** + * Submit update multiple sides request + * @param {object} sides + * @public + */ + updateSides = (sides) => { this.props.updateSides(sides, this.props.projectID, this.props.filters); } + + /** + * Returns items in common + * @param {array} list1 + * @param {array} list2 + * @public + */ + 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)}); + } + + /** + * Returns notes of currently selected objects + * @public + */ + getCommonNotes = () => { + // Find the common notes of all currently selected objects + const memberType = this.props.selectedObjects.type; + const members = this.props.selectedObjects.members; + let notes = this.props[memberType+"s"][members[0]].notes; + for (let id of members) { + notes = this.intersect(notes, this.props[memberType+"s"][id].notes); + } + return notes; + } + + updateNote = (noteID, note) => { + this.props.updateNote(noteID, note, this.props.projectID, this.props.filters); + } + + linkDialogNote = (noteID, objects) => { + this.props.linkNote(noteID, objects, this.props.projectID, this.props.filters); + } + + linkNote = (noteID) => { + let objects = []; + let type = this.props.selectedObjects.type; + if (type==="Recto" || type==="Verso") + type = "Side"; + for (let id of this.props.selectedObjects.members) { + objects.push({id, type}); + } + this.props.linkNote(noteID, objects, this.props.projectID, this.props.filters); + } + + linkAndUnlinkNotes = (noteID, linkObjects, unlinkObjects) => { + this.props.linkAndUnlinkNotes(noteID, linkObjects, unlinkObjects, this.props.projectID, this.props.filters); + } + + unlinkDialogNote = (noteID, objects) => { + this.props.unlinkNote(noteID, objects, this.props.projectID, this.props.filters); + } + + unlinkNote = (noteID, sideIndex) => { + let objects = []; + let type = this.props.selectedObjects.type; + if (type==="Recto" || type==="Verso") + type = "Side"; + for (let id of this.props.selectedObjects.members) { + objects.push({id, type}); + } + this.props.unlinkNote(noteID, objects, this.props.projectID, this.props.filters); + } + + createAndAttachNote = (noteTitle, noteType, description) => { + let objects = []; + let type = this.props.selectedObjects.type; + if (type==="Recto" || type==="Verso") + type = "Side"; + for (let id of this.props.selectedObjects.members) { + objects.push({id, type}); + } + let note = { + project_id: this.props.projectID, + title: noteTitle, + type: noteType, + description: description, + } + this.props.createAndAttachNote(note, objects, this.props.projectID, this.props.filters); + } + + deleteNote = (noteID) => { + this.props.deleteNote(noteID, this.props.projectID, this.props.filters) + } + + + handleChangeInfoBoxTab = (value, event) => { + this.props.changeInfoBoxTab( + value, + this.props.selectedObjects, + this.props.Leafs, + this.props.Rectos, + this.props.Versos, + ) + } + + filterActiveSide = (sideOrder) => { + let filteredSelectedObjects = {}; + for (let sideID in this.props.selectedObjects.members) { + let side = this.props.selectedObjects.members[sideID]; + if (side.order===sideOrder) + filteredSelectedObjects[sideID] = side; + } + return filteredSelectedObjects; + } + + + render() { + if (Object.keys(this.props.Groups).length===0){ + return ( +
+ this.toggleAddGroupDialog(true)} + /> + +
+ ); + } + + if (this.props.selectedObjects.members.length === 0){ + return (
); + } + + const leafSideTabs = ( + + + + + + ); + + const groupTab = ( + + + + ); + + const noteActions = { + updateNote: this.updateNote, + deleteNote: this.deleteNote, + linkNote: this.linkNote, + unlinkNote: this.unlinkNote, + linkAndUnlinkNotes: this.linkAndUnlinkNotes, + linkDialogNote: this.linkDialogNote, + unlinkDialogNote: this.unlinkDialogNote, + createAndAttachNote: this.createAndAttachNote + } + + if (this.props.selectedObjects.type === "Group") { + return ( +
+ {groupTab} + +
+ ); + } else if (this.props.selectedObjects.type === "Leaf") { + return ( +
+ {leafSideTabs} + +
+ ); + } else if (this.props.selectedObjects.type === "Recto") { + return ( +
+ {leafSideTabs} + +
+ ); + } else if (this.props.selectedObjects.type === "Verso") { + return ( +
+ {leafSideTabs} + +
+ ); + } else { + return (
); + } + } + static propTypes = { + /** Dictionary of actions */ + projectID: PropTypes.string, + } +} +const mapStateToProps = (state) => { + return { + projectID: state.active.project.id, + Groups: state.active.project.Groups, + Leafs: state.active.project.Leafs, + Rectos: state.active.project.Rectos, + Versos: state.active.project.Versos, + Notes: state.active.project.Notes, + noteTypes: state.active.project.noteTypes, + selectedObjects: state.active.collationManager.selectedObjects, + collationManager: state.active.collationManager, + notesManager: state.active.notesManager, + filters: state.active.collationManager.filters, + tacketing: state.active.collationManager.visualizations.tacketing, + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + toggleTacket: (toggle) => { + dispatch(toggleTacket(toggle)); + }, + + addLeafs: (leaf, additional, projectID, filters) => { + dispatch(addLeafs(leaf, additional)) + .then(()=> { + dispatch(flashLeaves({order: additional.order, ...additional})); + setTimeout(()=>dispatch({type: "UNFLASH"}), 3000); + }) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateLeaf: (leafID, leaf, projectID, filters) => { + dispatch(updateLeaf(leafID, leaf)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateLeafs: (leafs, projectID, filters) => { + dispatch(updateLeafs(leafs, projectID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + conjoinLeafs: (leafIDs, projectID, filters) => { + dispatch(conjoinLeafs(leafIDs)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteLeaf: (leafID, projectID, filters) => { + dispatch(deleteLeaf(leafID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteLeafs: (leafs, projectID, filters) => { + dispatch(deleteLeafs(leafs)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + addGroups: (group, additional, projectID, filters) => { + dispatch(addGroups(group, additional)) + .then(()=> { + dispatch(flashGroups({order: group.order, ...additional})); + setTimeout(()=>dispatch({type: "UNFLASH"}), 3000); + }) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateGroup: (groupID, group, projectID, filters) => { + dispatch(updateGroup(groupID, group)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateGroups: (groups, projectID, filters) => { + dispatch(updateGroups(groups)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteGroup: (groupID, projectID, filters) => { + dispatch(deleteGroup(groupID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteGroups: (groups, projectID, filters) => { + dispatch(deleteGroups(groups, projectID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateSide: (sideID, side, projectID, filters) => { + dispatch(updateSide(sideID, side)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + updateSides: (sides, projectID, filters) => { + dispatch(updateSides(sides)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + toggleVisibility: (memberType, attributeName, visibility) => { + dispatch(toggleVisibility(memberType, attributeName, visibility)); + }, + + changeInfoBoxTab: (newType, selectedObjects, Leafs, Rectos, Versos) => { + dispatch(changeInfoBoxTab(newType, selectedObjects, {Leafs, Rectos, Versos})); + }, + + updateNote: (noteID, note, projectID, filters) => { + dispatch(updateNote(noteID, note)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteNote: (noteID, projectID, filters) => { + dispatch(deleteNote(noteID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + createAndAttachNote: (note, objects, projectID, filters) => { + dispatch(addNote(note)) + .then((action)=> { + if ((action.type).includes("SUCCESS")){ + for (let noteID in action.payload.Notes){ + if (action.payload.Notes[noteID].title===note.title){ + dispatch(linkNote(noteID, objects)); + break; + } + } + } + }) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + linkNote: (noteID, object, projectID, filters) => { + dispatch(linkNote(noteID, object)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + unlinkNote: (noteID, object, projectID, filters) => { + dispatch(unlinkNote(noteID, object)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + linkAndUnlinkNotes: (noteID, linkObjects, unlinkObjects, projectID, filters) => { + if (linkObjects.length > 0 && unlinkObjects.length > 0){ + dispatch(linkNote(noteID, linkObjects)) + .then(action=>dispatch(unlinkNote(noteID, unlinkObjects))) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + else if (linkObjects.length > 0) { + dispatch(linkNote(noteID, linkObjects)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + else if (unlinkObjects.length > 0) { + dispatch(unlinkNote(noteID, unlinkObjects)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(InfoBox); diff --git a/viscoll-app/src/containers/InfoBox.md b/viscoll-app/src/containers/InfoBox.md new file mode 100644 index 00000000..02147303 --- /dev/null +++ b/viscoll-app/src/containers/InfoBox.md @@ -0,0 +1,6 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| activeBox | string | Tracks which info box is active: `LEAF`, `GROUP` or `SIDE` | +| addGroupDialogOpen | boolean | `true` would show the add group dialog | diff --git a/viscoll-app/src/containers/NotesManager.js b/viscoll-app/src/containers/NotesManager.js new file mode 100644 index 00000000..6a788028 --- /dev/null +++ b/viscoll-app/src/containers/NotesManager.js @@ -0,0 +1,271 @@ +import React, {Component} from 'react'; +import { connect } from "react-redux"; +import PropTypes from 'prop-types'; +import TopBar from "./TopBar"; +import Feedback from "./Feedback"; +import ManageNotes from "../components/notesManager/ManageNotes"; +import NoteType from "../components/notesManager/NoteType"; +import {Tabs, Tab} from 'material-ui/Tabs'; +import Panel from '../components/global/Panel'; +import topbarStyle from "../styles/topbar"; +import { + changeManagerMode, + changeNotesTab, +} from "../actions/editCollation/interactionActions"; +import { + addNote, + updateNote, + deleteNote, + createNoteType, + updateNoteType, + deleteNoteType, + linkNote, + unlinkNote +} from "../actions/editCollation/modificationActions"; +import { sendFeedback } from "../actions/userActions"; + +class NotesManager extends Component { + + constructor(props) { + super(props); + this.state = { + Notes: props.Notes, + value: "", + filterTypes: { + title: true, + type: true, + description: true, + } + }; + } + + componentWillReceiveProps(nextProps) { + this.setState({Notes: nextProps.Notes}, ()=>this.applyFilter()) + } + + applyFilter = () => { + this.filterNotes(this.state.value, this.state.filterTypes); + } + + onValueChange = (e, value) => { + this.setState({value}, ()=>this.applyFilter()) + } + + onTypeChange = (type, checked) => { + this.setState({filterTypes: {...this.state.filterTypes, [type]: checked}}, () => this.applyFilter()) + } + + + filterNotes = (value, filterTypes) => { + if (value==="") { + this.setState({Notes: this.props.Notes}); + } else { + let filteredNotes = {}; + let isNoneSelected = true; + for (let type of Object.keys(filterTypes)) { + if (filterTypes[type]){ + isNoneSelected = false; + break; + } + } + if (isNoneSelected) + filterTypes = {title: true, type: true, description: true}; + for (let noteID in this.props.Notes) { + const note = this.props.Notes[noteID] + for (let type of Object.keys(filterTypes)) { + if (filterTypes[type] && note[type].toUpperCase().includes(value.toUpperCase())) + if (filteredNotes[noteID]) + break; + else + filteredNotes[noteID] = note; + } + }; + this.setState({Notes: filteredNotes}); + } + + } + + /** + * Toggle notes filter panel + * @public + */ + toggleFilterDrawer = () => { + this.setState({filterOpen: !this.state.filterOpen}); + } + + updateNote = (noteID, note) => { + this.props.updateNote(noteID, note, this.props); + } + + linkNote = (noteID, object) => { + this.props.linkNote(noteID, object, this.props); + } + + unlinkNote = (noteID, object) => { + this.props.unlinkNote(noteID, object, this.props); + } + + linkAndUnlinkNotes = (noteID, linkObjects, unlinkObjects) => { + this.props.linkAndUnlinkNotes(noteID, linkObjects, unlinkObjects, this.props); + } + + render() { + let content = ""; + + if (this.props.activeTab==="MANAGE") { + content = + } else if (this.props.activeTab==="TYPES") { + content = this.props.deleteNoteType(noteTypes, this.props) }} + /> + } + + const sidebar = ( +
+
+ +
this.props.changeManagerMode("collationManager")} > + Collation +
+
this.props.changeManagerMode("notesManager")} > + Notes +
+
this.props.changeManagerMode("imageManager")} > + Images +
+
+
+ ); + + return ( +
+ + this.props.changeNotesTab(v)} + > + + + + + {sidebar} +
+ {content} +
+ this.props.sendFeedback(title, message, this.props.user.id)}} + /> +
+ ) + } + static propTypes = { + /** Current tab in notes manager */ + activeTab: PropTypes.string, + /** Active project ID */ + projectID: PropTypes.string, + } +} +const mapStateToProps = (state) => { + return { + user: state.user, + projectID: state.active.project.id, + Groups: state.active.project.Groups, + Leafs: state.active.project.Leafs, + Rectos: state.active.project.Rectos, + Versos: state.active.project.Versos, + Notes: state.active.project.Notes, + noteTypes: state.active.project.noteTypes, + activeTab: state.active.notesManager.activeTab, + notesManager: state.active.notesManager, + managerMode: state.active.managerMode, + }; +}; +const mapDispatchToProps = (dispatch) => { + return { + changeManagerMode: (managerMode) => { + dispatch(changeManagerMode(managerMode)); + }, + changeNotesTab: (tabName) => { + dispatch(changeNotesTab(tabName)); + }, + addNote: (note) => { + dispatch(addNote(note)) + }, + updateNote: (noteID, note, props) => { + dispatch(updateNote(noteID, note)) + }, + deleteNote: (noteID) => { + dispatch(deleteNote(noteID)); + }, + createNoteType: (noteType) => { + dispatch(createNoteType(noteType)); + }, + updateNoteType: (noteType) => { + dispatch(updateNoteType(noteType)); + }, + deleteNoteType: (noteType, props) => { + dispatch(deleteNoteType(noteType)) + }, + linkNote: (noteID, object, props) => { + dispatch(linkNote(noteID, object)) + }, + unlinkNote: (noteID, object, props) => { + dispatch(unlinkNote(noteID, object)) + }, + linkAndUnlinkNotes: (noteID, linkObjects, unlinkObjects, props) => { + if (linkObjects.length > 0 && unlinkObjects.length > 0){ + dispatch(linkNote(noteID, linkObjects)) + .then((action) => { + dispatch(unlinkNote(noteID, unlinkObjects)) + }) + } + else if (linkObjects.length > 0) { + dispatch(linkNote(noteID, linkObjects)) + } + else if (unlinkObjects.length > 0) { + dispatch(unlinkNote(noteID, unlinkObjects)) + } + }, + sendFeedback: (title, message, userID) => { + dispatch(sendFeedback(title, message, userID)) + } + }; +}; +export default connect(mapStateToProps, mapDispatchToProps)(NotesManager); diff --git a/viscoll-app/src/containers/Project.js b/viscoll-app/src/containers/Project.js new file mode 100644 index 00000000..8e6e0073 --- /dev/null +++ b/viscoll-app/src/containers/Project.js @@ -0,0 +1,88 @@ +import React, {Component} from 'react'; +import { connect } from "react-redux"; +import PropTypes from 'prop-types'; +import CollationManager from './CollationManager' +import NotesManager from './NotesManager'; +import ImageManager from './ImageManager'; +import LoadingScreen from "../components/global/LoadingScreen"; +import Notification from "../components/global/Notification"; +import Feedback from "./Feedback"; +import { loadProject } from "../actions/editCollation/modificationActions"; + + +/** Container for 'Manager (Collation or Notes or Image)', `LoadingScreen`, and `Notification`. */ +class Project extends Component { + + componentWillMount() { + const projectID = this.props.location.pathname.split("/")[2]; + this.props.user.authenticated ? this.props.loadProject(projectID) : this.props.history.push('/'); + } + + componentDidUpdate() { + if (!this.props.user.authenticated) this.props.history.push('/'); + } + + render() { + const collationManager = (); + const notesManager = (); + const imageManager = (); + let manager; + switch (this.props.managerMode) { + case "collationManager": + manager = collationManager; + break; + case "notesManager": + manager = notesManager; + break; + case "imageManager": + manager = imageManager; + break; + default: + // Must never reach here. + manager = (
Oh No !! Something went wrong
); + } + return ( +
+ {manager} + + + +
+ ) + } + + static propTypes = { + /** History object from React Router */ + history: PropTypes.object, + /** Location object from React Router */ + location: PropTypes.object, + /** User object from Redux store */ + user: PropTypes.object, + /** Boolean if loading screen should appear - from Redux store */ + loading: PropTypes.bool, + /** Notification message from Redux store */ + notification: PropTypes.string, + } +} + + +const mapStateToProps = (state) => { + return { + user: state.user, + managerMode: state.active.managerMode, + loading: state.global.loading, + notification: state.global.notification, + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + loadProject: (projectID) => { + dispatch(loadProject(projectID)) + } + }; +}; + + +export default connect(mapStateToProps, mapDispatchToProps)(Project); + diff --git a/viscoll-app/src/containers/Project.md b/viscoll-app/src/containers/Project.md new file mode 100644 index 00000000..d646f802 --- /dev/null +++ b/viscoll-app/src/containers/Project.md @@ -0,0 +1,6 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| leftSideBarOpen | boolean | `true` to show the left sidebar | +| filterOpen | boolean | `true` to show the filter panel | diff --git a/viscoll-app/src/containers/TopBar.js b/viscoll-app/src/containers/TopBar.js new file mode 100644 index 00000000..8bf739bc --- /dev/null +++ b/viscoll-app/src/containers/TopBar.js @@ -0,0 +1,165 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import Toolbar from 'material-ui/Toolbar'; +import {ToolbarGroup} from 'material-ui/Toolbar'; +import IconMenu from 'material-ui/IconMenu'; +import MenuItem from 'material-ui/MenuItem'; +import IconButton from 'material-ui/IconButton'; +import Avatar from 'material-ui/Avatar'; +import UserProfileForm from '../components/topbar/UserProfileForm'; +import FlatButton from 'material-ui/FlatButton'; +import NotesFilter from "../components/notesManager/NotesFilter"; +import FilterIcon from 'material-ui/svg-icons/content/filter-list'; +import imgLogo from '../assets/logo_white.svg'; + +import { connect } from "react-redux"; +import { + logout, + updateProfile, + deleteProfile +} from "../actions/userActions"; + +/** The topbar menu used in `Dashboard` and `Project` components */ +class TopBar extends Component { + + constructor(props) { + super(props); + this.state = { + userProfileModalOpen: false + }; + } + + /** + * Pass the user object to the `updateProfile` action + * @param {object} user + * @public + */ + handleUserProfileUpdate = (user) => { + const userID = this.props.user.id; + this.props.updateProfile(user, userID); + } + + /** + * Toggle user profile modal + * @param {boolean} userProfileModalOpen + * @public + */ + toggleUserProfile = (userProfileModalOpen=false) => { + this.setState({ userProfileModalOpen }); + } + + /** + * Delete user account + * @public + */ + handleUserAccountDelete = () => { + const userID = this.props.user.id; + this.props.deleteProfile(userID); + } + + /** + * Log out user + * @public + */ + handleUserLogout = () => { + this.props.logoutUser(); + } + + /** + * Redirect to dashboard + * @public + */ + goHome = () => { + this.props.history.push('/dashboard'); + } + + render() { + // User icon menu on the right corner of Toolbar + let UserMenu; + if (this.props.user.name) { + UserMenu = ( + {this.props.user.name.charAt(0).toUpperCase()} } + targetOrigin={{horizontal: 'right', vertical: 'top'}} + anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} + > + this.toggleUserProfile(true)} /> + + + ); + } + + return ( +
+
+ Logo +
+ + + {this.props.children} + + + {this.props.toggleFilterDrawer? + } + > + + : null + } + {this.props.notesFilter ? : null} + {UserMenu} + + + +
+ ) + } + static propTypes = { + /** A set of Tabs content to display */ + children: PropTypes.object, + /** User object from Redux store */ + user: PropTypes.object, + } +} +const mapStateToProps = (state) => { + return { + user: state.user, + notes: state.active.notes + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + logoutUser: () => { + dispatch(logout()); + }, + updateProfile: (user, userID) => { + dispatch(updateProfile(user, userID)); + }, + deleteProfile: (userID) => { + dispatch(deleteProfile(userID)); + }, + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(TopBar); + + diff --git a/viscoll-app/src/containers/TopBar.md b/viscoll-app/src/containers/TopBar.md new file mode 100644 index 00000000..718f6660 --- /dev/null +++ b/viscoll-app/src/containers/TopBar.md @@ -0,0 +1,5 @@ +##### LOCAL STATES + +| Name | Type | Description | +|---|---|---| +| userProfileModalOpen | boolean | `true` would show the user profile modal | diff --git a/viscoll-app/src/helpers/MultiSelectAutoComplete.js b/viscoll-app/src/helpers/MultiSelectAutoComplete.js new file mode 100644 index 00000000..70f7b8d8 --- /dev/null +++ b/viscoll-app/src/helpers/MultiSelectAutoComplete.js @@ -0,0 +1,29 @@ +import React from 'react'; +import SuperSelectField from 'material-ui-superselectfield'; + + +const selectionsRenderer = (values) => { + if (values.length===1) return "1 item selected" + if (values.length>1) return `${values.length} items selected` + return "Select item(s)..." +} + + +const MultiSelectAutoComplete = (props) => { + return ( + + {props.children} + + ); +} + +export default MultiSelectAutoComplete; diff --git a/viscoll-app/src/helpers/getLeafsOfGroup.js b/viscoll-app/src/helpers/getLeafsOfGroup.js new file mode 100644 index 00000000..b53b2296 --- /dev/null +++ b/viscoll-app/src/helpers/getLeafsOfGroup.js @@ -0,0 +1,11 @@ +export function getLeafsOfGroup(group, Leafs){ + let leafMembersOfCurrentGroup = []; + leafMembersOfCurrentGroup.push({ + order: "None", + id: null + }) + for (let memberID of group.memberIDs) { + if (memberID.charAt(0)==="L") leafMembersOfCurrentGroup.push(Leafs[memberID]); + } + return leafMembersOfCurrentGroup; +} diff --git a/viscoll-app/src/index.js b/viscoll-app/src/index.js new file mode 100644 index 00000000..32e45730 --- /dev/null +++ b/viscoll-app/src/index.js @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './containers/App'; +import registerServiceWorker from './registerServiceWorker'; +import './styles/index.css'; + + +ReactDOM.render( + , + document.getElementById('root') +); +registerServiceWorker(); + diff --git a/viscoll-app/src/reducers/editCollationReducer.js b/viscoll-app/src/reducers/editCollationReducer.js new file mode 100644 index 00000000..460ab96e --- /dev/null +++ b/viscoll-app/src/reducers/editCollationReducer.js @@ -0,0 +1,328 @@ +import { initialState } from './initialStates/active'; + +export default function editCollationReducer(state=initialState, action) { + try { + if (action.error) { + if (action.error.status===0) return initialState; + action = {type: action.type, payload: action.error.response.data} + } + } catch (e) { } + switch(action.type) { + // MODIFICATIONS + case "LOAD_PROJECT_SUCCESS": + case "ADD_LEAF(S)_SUCCESS": + case "ADD_GROUP(S)_SUCCESS": + case "UPDATE_GROUP_SUCCESS": + case "UPDATE_GROUPS_SUCCESS": + case "UPDATE_LEAF_SUCCESS": + case "UPDATE_LEAFS_SUCCESS": + case "UPDATE_SIDE_SUCCESS": + case "UPDATE_SIDES_SUCCESS": + case "CREATE_NOTE_SUCCESS": + case "UPDATE_NOTE_SUCCESS": + case "DELETE_NOTE_SUCCESS": + case "LINK_NOTE_SUCCESS": + case "UNLINK_NOTE_SUCCESS": + case "CREATE_NOTETYPE_SUCCESS": + case "UPDATE_NOTETYPE_SUCCESS": + case "DELETE_NOTETYPE_SUCCESS": + case "UPDATE_MANIFEST_SUCCESS": + case "DELETE_MANIFEST_SUCCESS": + case "MAP_SIDES_SUCCESS": + state = { + ...state, + project: action.payload + }; + break; + case "CREATE_MANIFEST_SUCCESS": + state = { + ...state, + project: action.payload, + imageManager: { + ...state.imageManager, + manageSources: { + ...state.imageManager.manageSources, + error: "" + } + } + } + break; + case "DELETE_LEAF_SUCCESS": + case "DELETE_LEAFS_SUCCESS": + case "DELETE_GROUP_SUCCESS": + case "DELETE_GROUPS_SUCCESS": + state = { + ...state, + project: action.payload, + collationManager: { + ...state.collationManager, + selectedObjects: initialState.collationManager.selectedObjects + } + }; + break; + case "TOGGLE_TACKET": + state = { + ...state, + collationManager: { + ...state.collationManager, + visualizations: { + ...state.collationManager.visualizations, + tacketing: action.payload, + } + } + } + break; + case "CREATE_MANIFEST_FAILED": + state = { + ...state, + imageManager: { + ...state.imageManager, + manageSources: { + ...state.imageManager.manageSources, + error: action.payload.errors + } + } + } + break; + case "CANCEL_CREATE_MANIFEST": + state = { + ...state, + imageManager: { + ...state.imageManager, + manageSources: { + ...state.imageManager.manageSources, + error: "" + } + } + } + break; + case "LOGOUT_SUCCESS": + case "DELETE_PROFILE_SUCCESS": + case "LOAD_PROJECTS_SUCCESS": + state = initialState; + break; + case "LOAD_PROJECT_FAILED": + case "LOAD_NOTES_FAILED": + case "UPDATE_GROUP_FAILED": + case "UPDATE_GROUPS_FAILED": + case "UPDATE_SIDE_FAILED": + case "UPDATE_SIDES_FAILED": + case "UPDATE_LEAF_FAILED": + case "UPDATE_LEAFS_FAILED": + case "ADD_LEAF(S)_FAILED": + case "ADD_GROUP(S)_FAILED": + case "DELETE_LEAF_FAILED": + case "DELETE_LEAFS_FAILED": + case "DELETE_GROUP_FAILED": + case "DELETE_GROUPS_FAILED": + case "DELETE_NOTE_FAILED": + case "FILTER_PROJECT_FAILED": + case "UPDATE_FILTER_QUERY_FAILED": + case "UPDATE_FILTER_SELECTION_FAILED": + case "CREATE_NOTE_FAILED": + case "UPDATE_NOTE_FAILED": + case "CREATE_NOTETYPE_FAILED": + case "UPDATE_NOTETYPE_FAILED": + case "DELETE_NOTETYPE_FAILED": + case "EXPORT_FAILED": + case "UPDATE_MANIFEST_FAILED": + case "DELETE_MANIFEST_FAILED": + case "MAP_SIDES_FAILED": + break; + + // INTERACTIONS + case "persist/REHYDRATE": + state = {...state, ...action.payload.active} + break; + case "CHANGE_VIEW_MODE": + state = { + ...state, + collationManager: { + ...state.collationManager, + viewMode: action.payload + } + } + break; + case "CHANGE_MANAGER_MODE": + state = {...state, managerMode: action.payload } + break; + case "CHANGE_NOTES_TAB": + state = { + ...state, + notesManager: { + ...state.notesManager, + activeTab: action.payload + } + } + break; + case "CHANGE_IMAGES_TAB": + state = { + ...state, + imageManager: { + ...state.imageManager, + activeTab: action.payload + } + } + break; + case "TOGGLE_FILTER_PANEL": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...state.collationManager.filters, + filterPanelOpen: action.payload + } + } + } + break; + case "TOGGLE_SELECTED_OBJECTS": + case "UPDATE_CURRENT_SELECTED_OBJECTS": + state = { + ...state, + collationManager: { + ...state.collationManager, + selectedObjects: action.payload + } + } + break; + case "TOGGLE_VISIBILITY": + state = { + ...state, + collationManager: { + ...state.collationManager, + visibleAttributes: { + ...state.collationManager.visibleAttributes, + [action.payload.memberType]: { + ...state.collationManager.visibleAttributes[action.payload.memberType], + [action.payload.attributeName]: action.payload.newValue + } + } + } + } + break; + case "FILTER_PROJECT_SUCCESS": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...state.collationManager.filters, + ...action.payload, + active: true + }, + visibleAttributes: action.payload.visibleAttributes + } + } + delete state["collationManager"]["filters"]["visibleAttributes"]; + break; + case "RESET_FILTERS": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...initialState.collationManager.filters, + filterPanelOpen: true, + queries: action.payload + }, + selectedObjects: initialState.collationManager.selectedObjects + } + } + break; + case "TOGGLE_FILTER_DISPLAY": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...state.collationManager.filters, + hideOthers: !state.collationManager.filters.hideOthers + } + } + + } + break; + case "UPDATE_FILTER_QUERY": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...state.collationManager.filters, + queries: action.payload + } + } + } + break; + case "UPDATE_FILTER_SELECTION": + state = { + ...state, + collationManager: { + ...state.collationManager, + filters: { + ...state.collationManager.filters, + selection: action.payload.selection, + hideOthers: false + }, + selectedObjects: action.payload.selectedObjects, + } + } + break; + case "FLASH_LEAVES": + state = { + ...state, + collationManager: { + ...state.collationManager, + flashItems: { + ...state.collationManager.flashItems, + leaves: action.payload + } + } + } + break; + case "FLASH_GROUPS": + state = { + ...state, + collationManager: { + ...state.collationManager, + flashItems: { + ...state.collationManager.flashItems, + groups: action.payload + } + } + } + break; + case "UNFLASH": + state = { + ...state, + collationManager: { + ...state.collationManager, + flashItems: { + groups: [], + leaves: [] + } + } + } + break; + case "EXPORT_SUCCESS": + let exportedData = action.payload; + console.log() + if (action.payload.type==="xml"){ + exportedData = action.payload.data; + } else if (action.payload.type==="formula") { + exportedData = action.payload.data; + } else { + exportedData = JSON.stringify(exportedData, null, 4); + } + state = { + ...state, + exportedData + } + break; + default: + break; + } + return state; +} + diff --git a/viscoll-app/src/reducers/globalReducer.js b/viscoll-app/src/reducers/globalReducer.js new file mode 100644 index 00000000..3f4572cc --- /dev/null +++ b/viscoll-app/src/reducers/globalReducer.js @@ -0,0 +1,30 @@ +import { initialState } from './initialStates/global'; + + +export default function projectReducer(state=initialState, action) { + switch(action.type) { + case "persist/REHYDRATE": + state = initialState + break; + case "SHOW_LOADING": + state = {...state, loading: true} + break; + case "HIDE_LOADING": + state = {...state, loading: false} + break; + case "SHOW_NOTIFICATION": + state = {...state, notification: action.payload} + break; + case "HIDE_NOTIFICATION": + state = {...state, notification: ""} + break; + case "DELETE_PROFILE_SUCCESS": + case "LOGOUT_SUCCESS": + state = initialState + break; + default: + break; + } + return state; +} + diff --git a/viscoll-app/src/reducers/initialStates/active.js b/viscoll-app/src/reducers/initialStates/active.js new file mode 100644 index 00000000..b217d58d --- /dev/null +++ b/viscoll-app/src/reducers/initialStates/active.js @@ -0,0 +1,190 @@ +export const initialState = { + project: { + id: "", + title: "", + shelfmark: "", + uri: "", + metadata: { + date: "" + }, + manifests: {}, + groupIDs: [], + leafIDs: [], + rectoIDs: [], + versoIDs: [], + Groups: {}, + Leafs: {}, + Rectos: {}, + Versos: {}, + noteTypes: [], + Notes: {}, + preferences: { + showTips: true + } + }, + + managerMode: "collationManager", + collationManager: { + selectedObjects: { + type: "", + members: [], + lastSelected: "" + }, + 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', 'Flyleaf', 'Endleaf', 'Replaced'], + isDropdown: true, + }, + { + name: 'material', + displayName: 'Material', + options: ['None', 'Parchment', 'Paper', 'Other'], + isDropdown: true, + }, + { + name: 'conjoined_leaf_order', + displayName: 'Conjoined To', + isDropdown: true, + }, + { + name: 'attached_above', + displayName: 'Attached Above', + options: ['None', 'Glued', 'Other'], + isDropdown: true, + }, + { + name: 'attached_below', + displayName: 'Attached Below', + options: ['None', 'Glued', '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: { + tacketing: "", + } + }, + notesManager: { + activeTab: "MANAGE", + }, + imageManager: { + activeTab: "MANAGE", + manageSources: { + error: "" + } + }, + exportedData: "" +}; + + +export default initialState; diff --git a/viscoll-app/src/reducers/initialStates/global.js b/viscoll-app/src/reducers/initialStates/global.js new file mode 100644 index 00000000..a2094829 --- /dev/null +++ b/viscoll-app/src/reducers/initialStates/global.js @@ -0,0 +1,6 @@ +export const initialState = { + loading: false, + notification: "" +}; + +export default initialState; diff --git a/viscoll-app/src/reducers/initialStates/projects.js b/viscoll-app/src/reducers/initialStates/projects.js new file mode 100644 index 00000000..73bd5836 --- /dev/null +++ b/viscoll-app/src/reducers/initialStates/projects.js @@ -0,0 +1,6 @@ +export const initialState = { + projects: [], + importStatus: null +}; + +export default initialState; diff --git a/viscoll-app/src/reducers/initialStates/user.js b/viscoll-app/src/reducers/initialStates/user.js new file mode 100644 index 00000000..dd93ac08 --- /dev/null +++ b/viscoll-app/src/reducers/initialStates/user.js @@ -0,0 +1,12 @@ +export const initialState = { + authenticated: false, + token: "", + errors: { + login: {errorMessage: ""}, + register: {email: "", password: ""}, + update: {password: "", current_password: "", email: ""}, + confirmation: "", + } +} + +export default initialState; diff --git a/viscoll-app/src/reducers/projectReducer.js b/viscoll-app/src/reducers/projectReducer.js new file mode 100644 index 00000000..642f4060 --- /dev/null +++ b/viscoll-app/src/reducers/projectReducer.js @@ -0,0 +1,52 @@ +import { initialState } from './initialStates/projects'; + +export default function projectReducer(state=initialState, action) { + try { + if (action.error) { + if (action.error.status===0) return initialState; + action = {type: action.type, payload: action.error.response.data} + } + } catch (e) {} + + switch(action.type) { + case "persist/REHYDRATE": + if (action.payload.projects){ + state = {projects: action.payload.projects.projects, importStatus: null} + } + break; + case "LOAD_PROJECTS_SUCCESS": + case "CREATE_PROJECT_SUCCESS": + case "UPDATE_PROJECT_SUCCESS": + case 'DELETE_PROJECT_SUCCESS': + case "CLONE_PROJECT_IMPORT_SUCCESS": + case "IMPORT_MANIFEST_SUCCESS": + state = {projects: action.payload} + break; + case "IMPORT_PROJECT_SUCCESS": + state = {projects: action.payload, importStatus: "SUCCESS"} + break; + case "IMPORT_PROJECT_SUCCESS_CALLBACK": + state = {...state, importStatus: null} + break; + case "LOGOUT_SUCCESS": + case "DELETE_PROFILE_SUCCESS": + state = initialState + break; + case "IMPORT_PROJECT_FAILED": + state = { + ...state, + importStatus: action.payload.error + } + break; + case "CREATE_PROJECT_FAILED": + case "UPDATE_PROJECT_FAILED": + case "DELETE_PROJECT_FAILED": + case "LOAD_PROJECTS_FAILED": + case "CLONE_PROJECT_IMPORT_FAILED": + break; + default: + break; + } + return state; +} + diff --git a/viscoll-app/src/reducers/userReducer.js b/viscoll-app/src/reducers/userReducer.js new file mode 100644 index 00000000..b92caf38 --- /dev/null +++ b/viscoll-app/src/reducers/userReducer.js @@ -0,0 +1,98 @@ +import { initialState } from './initialStates/user'; + +export default function userReducer(state=initialState, action) { + try { + if (action.error) { + if (action.error.status===0) return initialState; + action = {type: action.type, payload: action.error.response.data} + } + } catch (e) {} + switch(action.type) { + case "persist/REHYDRATE": + state = {...state, ...action.payload.user, errors: initialState.errors} + delete state.registerSuccess + break; + case "LOGIN_SUCCESS": + state = { + ...state, + id: action.payload.session.id, + name: action.payload.session.name, + email: action.payload.session.email, + token: action.payload.session.jwt, + authenticated: true, + lastLoggedIn: action.payload.session.lastLoggedIn, + preferences: action.payload.session.preferences, + } + break; + case "LOGIN_FAILED": + state = { + ...state, + errors: { + ...state.errors, + login: { + errorMessage: action.payload.errors.session + }, + } + } + break; + case "REGISTER_SUCCESS": + state = { + ...state, + registerSuccess: true + } + break; + case "REGISTER_FAILED": + state = { + ...state, + errors: { + ...state.errors, + register: action.payload.errors + } + } + break; + case "UPDATE_PROFILE_SUCCESS": + state = { + ...state, + errors: initialState.errors, + ...action.payload + } + break; + case "UPDATE_PROFILE_FAILED": + state = { + ...state, + errors: { + ...state.errors, + update: {...state.errors.update, ...action.payload} + } + } + break; + case "LOGOUT_SUCCESS": + case "DELETE_PROFILE_SUCCESS": + state = initialState + break; + case "CONFIRM_SUCCESS": + case "REQUEST_RESET_SUCCESS": + case "REQUEST_RESET_FAILED": + case "RESET_SUCCESS": + case "RESET_FAILED": + case "LOGOUT_FAILED": + case "DELETE_PROFILE_FAILED": + break; + case "CONFIRM_FAILED": + let errorMessage = "Error confirming your account!"; + if (action.payload.errors.confirmation_token.length>0) { + errorMessage = "Confirmation token " + action.payload.errors.confirmation_token[0]; + } + state = { + ...state, + errors: { + ...state.errors, + confirmation: errorMessage, + } + } + break; + default: + break; + } + return state; +} diff --git a/viscoll-app/src/registerServiceWorker.js b/viscoll-app/src/registerServiceWorker.js new file mode 100644 index 00000000..cdaa1e9c --- /dev/null +++ b/viscoll-app/src/registerServiceWorker.js @@ -0,0 +1,49 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +export default function register() { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); + }); + } +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/viscoll-app/src/store.js b/viscoll-app/src/store.js new file mode 100644 index 00000000..a3c36445 --- /dev/null +++ b/viscoll-app/src/store.js @@ -0,0 +1,39 @@ +import { createStore, combineReducers, compose, applyMiddleware } from "redux"; +import {autoRehydrate} from 'redux-persist' +import user from "./reducers/userReducer"; +import projects from "./reducers/projectReducer"; +import active from "./reducers/editCollationReducer"; +import global from "./reducers/globalReducer"; +import axiosMiddleware from 'redux-axios-middleware'; +import { client, clientOptions } from './axiosConfig'; + +let storeEnhancers; +if (process.env.NODE_ENV === 'development'){ + storeEnhancers = compose( + applyMiddleware( + axiosMiddleware(client, clientOptions) + ), + autoRehydrate(), + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() + ) +} else { + storeEnhancers = compose( + applyMiddleware( + axiosMiddleware(client, clientOptions) + ), + autoRehydrate() + ) +} + +const store = createStore( + combineReducers({ + user, + projects, + active, + global + }), + {}, + storeEnhancers +); + +export default store; diff --git a/viscoll-app/src/styles/App.css b/viscoll-app/src/styles/App.css new file mode 100644 index 00000000..4e7c3b3c --- /dev/null +++ b/viscoll-app/src/styles/App.css @@ -0,0 +1,814 @@ +.landing { + width: 100vw; + height: 100vh; + background: #2B4352; + text-align: center; } + .landing .container { + margin: 0 auto; + width: 80vw; + height: 90vh; + display: flex; + align-items: center; + align-content: center; } + .landing img { + width: 100%; } + .landing .panelLogo { + width: 55%; } + .landing .panelLogin { + width: 40%; + padding-left: 5%; } + .landing .panelBottom { + width: 100%; + height: 10vh; + background: #4ED6CB; } + .landing hr { + border: 1px solid #4ED6CB; } + .landing .spacingBottom { + margin-bottom: 1.5em; } + .landing .spacingTop { + margin-top: 1.5em; } + .landing p { + color: #4ED6CB; } + .landing a { + color: #4ED6CB; + cursor: pointer; + text-decoration: underline; } + .landing a:hover { + color: #a1e9e3; } + +.sidebar { + position: fixed; + display: block; + top: 55px; + width: 18%; + height: 100%; + background: #3A4B55; + opacity: 1; + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; } + .sidebar.hidden { + opacity: 0; } + .sidebar hr { + border: 1px solid #4ED6CB; + margin: 0; } + .sidebar h1 { + color: #FFFFFF; + font-size: 1em; + font-weight: lighter; } + .sidebar .selectMode { + padding: 1em 1em 2em 1em; + text-align: center; + background: #34434c; } + .sidebar .selectMode span { + font-size: 13px; + color: #4ED6CB; + text-transform: uppercase; } + .sidebar .selectMode .tip { + font-size: 0.8em; + color: #F2F2F2; + text-align: left; + line-height: 1.5em; } + .sidebar .selectMode .close { + text-align: right; + margin-right: -10px; + margin-top: -10px; } + .sidebar .manager { + text-align: center; + padding: 0.5em 0em; + cursor: pointer; + margin: 0px -16px; + text-transform: uppercase; + -webkit-transition: all 100ms ease-in-out; + -ms-transition: all 100ms ease-in-out; + transition: all 100ms ease-in-out; } + .sidebar .manager:hover { + background: rgba(255, 255, 255, 0.02); + font-weight: bold; } + .sidebar .manager.active { + background: rgba(255, 255, 255, 0.05); + font-weight: bold; + border-left: 3px solid #4ED6CB; } + .sidebar .export div + div { + margin-top: 10px; } + +.feedback { + position: fixed; + bottom: 0; + left: 0; + width: 18%; + z-index: 10000; + text-align: center; } + +.editIcon { + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; + background: #BABABA !important; } + .editIcon:hover { + background: #A5A5A5 !important; + cursor: pointer; } + +.projectPanelInfo { + padding: 1em; + line-height: 2em; } + .projectPanelInfo .info { + padding-top: 1em; + font-size: 0.9em; } + .projectPanelInfo .info span { + color: rgba(78, 78, 78, 0.8); } + +.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%; + -webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); + box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); } + .infoBox .inner { + padding: 10px 20px 15px 20px; } + .infoBox .inner .row { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; } + .infoBox .inner .label { + width: 35%; } + .infoBox .inner .input { + width: 55%; } + +.workspace, .projectWorkspace, .dashboardWorkspace, .notesWorkspace, .imageWorkspace { + position: absolute; + left: 18%; + top: 56px; } + +.projectWorkspace { + width: 54%; + margin: 2%; } + .projectWorkspace .viewingMode { + display: flex; } + .projectWorkspace .viewingMode > div:first-child { + margin-top: 4px; + margin-left: 15px; } + .projectWorkspace .viewingMode > div:nth-child(2) { + margin-top: 14px; } + +.dashboardWorkspace { + width: 82%; + -webkit-transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1); + -ms-transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1); + transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1); } + .dashboardWorkspace.projectPanelOpen { + width: 70%; } + +.notesWorkspace, .imageWorkspace { + width: 82%; } + +.itemContainer { + display: flex; + align-items: stretch; } + .itemContainer.group { + margin-top: -21px; + -webkit-transition: background 100ms ease-in-out; + -ms-transition: background 100ms ease-in-out; + transition: background 100ms ease-in-out; } + .itemContainer.group:hover { + background: #caf3ef !important; + cursor: pointer; } + .itemContainer.group.active:hover { + background: #4ED6CB !important; } + +.leafSection { + display: flex; + flex-grow: 1; + border: 1px solid #FFFFFF; + -webkit-transition: background 100ms ease-in-out; + -ms-transition: background 100ms ease-in-out; + transition: background 100ms ease-in-out; } + .leafSection:hover { + background: #caf3ef; + cursor: pointer; } + .leafSection.active:hover { + background: #4ED6CB !important; } + +.itemName { + width: 70px; + display: flex; + align-items: center; + font-weight: 500; + padding-left: 20px; + min-height: 45px; } + +.itemAttributes { + flex-grow: 4; + display: flex; } + +.attribute { + display: flex; + align-items: center; + max-width: 100px; + padding: 0.5em 0.8em; + color: rgba(78, 78, 78, 0.6); + font-weight: 400; + border-left: 1px solid rgba(78, 78, 78, 0.05); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + .attribute span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11px; } + .attribute span:nth-child(1) { + color: rgba(78, 78, 78, 0.4); + display: block; } + .attribute.active, .attribute:hover { + color: #4e4e4e; } + .attribute.active.small { + color: #4e4e4e; } + .attribute.active span:nth-child(1) { + color: rgba(78, 78, 78, 0.7); } + +.sideSection { + flex-grow: 1; + display: flex; + flex-direction: column; + border-left: 1px solid rgba(78, 78, 78, 0.15); } + .sideSection .side { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + border: 1px solid #FFFFFF; } + .sideSection .side:hover { + background: #caf3ef; + cursor: pointer; } + .sideSection .side .name { + width: 40px; + display: inline-block; + padding: 7px 10px 0px 10px; + vertical-align: top; + font-weight: normal; + border-right: 1px solid rgba(78, 78, 78, 0.05); } + .sideSection .side .attribute { + display: inline-block; + vertical-align: top; + padding: 0px 10px; + padding-top: 2px; + border-right: 1px solid rgba(78, 78, 78, 0.05); + color: rgba(78, 78, 78, 0.6); + -webkit-transition: color 200ms ease-in-out; + -ms-transition: color 200ms ease-in-out; + transition: color 200ms ease-in-out; + font-size: 13px; } + .sideSection .side .attribute span { + font-weight: normal; + color: rgba(78, 78, 78, 0.6); } + .sideSection .side .attribute:hover { + color: #4e4e4e; } + .sideSection .side .attribute.active { + color: #4e4e4e; } + .sideSection .side:first-child { + border-bottom: 1px solid rgba(78, 78, 78, 0.15); } + +.sideToggle { + width: 20px; + height: 44px; + border-left: 1px solid rgba(78, 78, 78, 0.15); } + .sideToggle .side { + display: block; + width: 20px; + height: 20px; + padding-top: 2px; + padding-left: 5px; + color: rgba(78, 78, 78, 0.6); } + .sideToggle .side:first-child { + border-bottom: 1px solid rgba(78, 78, 78, 0.15); } + .sideToggle .side:hover { + background: #a1e9e3; + cursor: pointer; + color: #4e4e4e; } + +.flash { + animation-name: flashify; + animation-duration: 3s; } + +@-webkit-keyframes flashify { + 0% { + border: 2px solid white; } + 35% { + border: 2px solid #4ed6cb; } + 80% { + border: 2px solid #4ed6cb; } + 100% { + border: 2px solid white; } } +@-moz-keyframes flashify { + 0% { + border: 2px solid white; } + 35% { + border: 2px solid #4ed6cb; } + 80% { + border: 2px solid #4ed6cb; } + 100% { + border: 2px solid white; } } +@-ms-keyframes flashify { + 0% { + border: 2px solid white; } + 35% { + border: 2px solid #4ed6cb; } + 80% { + border: 2px solid #4ed6cb; } + 100% { + border: 2px solid white; } } +@keyframes flashify { + 0% { + border: 2px solid white; } + 35% { + border: 2px solid #4ed6cb; } + 80% { + border: 2px solid #4ed6cb; } + 100% { + border: 2px solid white; } } +.topbar { + position: fixed; + left: 0px; + width: 100%; + z-index: 100; + -webkit-box-shadow: 0px 1px 2px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0px 1px 2px 1px rgba(0, 0, 0, 0.05); } + .topbar .logo { + float: left; + width: 18%; + height: 55px; + text-align: center; + position: relative; + background: #3A4B55; } + .topbar .logo img { + width: 60%; + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); } + +.notesManager { + height: 100%; } + .notesManager .container { + padding: 1em 2em 0em 2em; } + .notesManager .browse { + height: 100%; } + .notesManager .browse .notesList .item { + margin: 1em; + padding: 1em; + display: block; + background: #F2F2F2; + cursor: pointer; + -webkit-transition: background 200ms ease-in-out; + -ms-transition: background 200ms ease-in-out; + transition: background 200ms ease-in-out; } + .notesManager .browse .notesList .item:hover { + background: #fcfcfc; } + .notesManager .browse .notesList .item.active { + font-weight: 600; + background: #4ED6CB; } + .notesManager .browse .notesList .item.add { + border: 1px solid #93dca6; + background: white; } + .notesManager .browse .notesList .item.add.active { + background: #34A251; + color: #FFFFFF; } + .notesManager .browse .details { + position: relative; + left: 256px; + width: 65%; } + .notesManager .noteType { + padding: 1em 2em; } + .notesManager .noteType .items { + display: flex; + flex-wrap: wrap; } + .notesManager .noteType .item { + width: 220px; + margin-right: 1em; } + .notesManager .noteType .create { + display: flex; + margin-bottom: 2em; } + .notesManager .noteType .create .input { + margin-right: 1em; } + +.notesInfobox { + display: flex; + flex-wrap: wrap; } + +.noteSearch { + height: 56px; } + .noteSearch .searchTextbox { + padding-top: 5px; } + +.searchOptions { + visibility: hidden; + opacity: 0; + background: #FFFFFF; + -webkit-border-radius: 0px 0px 6px 6px; + -moz-border-radius: 0px 0px 6px 6px; + -ms-border-radius: 0px 0px 6px 6px; + border-radius: 0px 0px 6px 6px; + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; + padding: 0em 1em; } + .searchOptions.active { + visibility: visible; + opacity: 1; } + +.noteForm { + width: 100%; + margin-left: 2%; + display: flex; + flex-wrap: wrap; + align-items: flex-start; } + .noteForm .label { + padding-top: 1em; + width: 20%; } + .noteForm .input { + width: 80%; } + .noteForm .input .textOnly { + margin-top: 1em; + color: #4e4e4e; } + .noteForm .buttons { + text-align: right; + width: 100%; + padding-top: 2em; } + .noteForm .objectAttachments { + width: 100%; } + +.filter { + width: 100%; + max-height: 45%; + position: relative; + z-index: 2; + left: 0; + display: flex; + justify-content: flex-end; + -webkit-transition: opacity 200ms linear; + -ms-transition: opacity 200ms linear; + transition: opacity 200ms linear; } + +.filterContainer { + border-top: 1px solid #F2F2F2; + position: fixed; + width: 82%; + max-height: 45%; + background: #FFFFFF; + padding-bottom: 10px; + overflow: auto; + -webkit-transition: top 450ms cubic-bezier(0.23, 1, 0.32, 1); + -ms-transition: top 450ms cubic-bezier(0.23, 1, 0.32, 1); + transition: top 450ms cubic-bezier(0.23, 1, 0.32, 1); + -webkit-box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.3); + box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.3); } + +.filterRow { + display: flex; + align-items: flex-start; + justify-content: center; } + .filterRow + .filterRow { + margin-top: -20px; } + .filterRow .filterField { + width: 230px; + margin-left: 10px; } + .filterRow .filterField:first-child { + margin-left: 20px; } + .filterRow .filterField:last-child { + width: 160px; + text-align: center; } + +.filterMessage { + text-transform: uppercase; + font-size: 0.9em; + font-weight: 500; + color: #727272; } + +.appLoading { + width: 100vw; + height: 100vh; + background: #3A4B55; + display: flex; + align-items: center; } + .appLoading .container { + width: 100vw; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: -5%; } + .appLoading .logo { + width: 30%; + max-width: 300px; + margin-bottom: 1em; } + +.fourOhFour { + width: 100vw; + height: 100vh; + background: #3A4B55; + display: flex; + align-items: center; } + .fourOhFour .container { + width: 100vw; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: -10%; } + .fourOhFour .container h1 { + font-size: 8em; + color: #FFFFFF; + padding-bottom: 0; + margin-bottom: 10px; } + .fourOhFour .container p { + color: #FFFFFF; + margin-bottom: 30px; } + +.imageManager .form .row { + display: flex; } + .imageManager .form .row .label { + padding-top: 1em; + min-width: 120px; } + .imageManager .form .row .input { + flex-grow: 1; } +.imageManager .manageManifests { + padding: 0em 2em; } + .imageManager .manageManifests .manifestCard { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + padding: 1.5em 1.5em 1em 1.5em; + font-size: 1.1em; + font-weight: 500; } + .imageManager .manageManifests .manifestCard span { + font-size: 0.8em; + font-weight: normal; + color: rgba(78, 78, 78, 0.6); } + .imageManager .manageManifests .manifestCard > div { + flex-grow: 1; } + .imageManager .manageManifests .manifestCard .thumbnails { + text-align: right; } +.imageManager .imageMapper .draggableItem { + height: 50px; + background: #FFFFFF; + border-width: 0px 1px 0px 1px; + border-style: solid; + border-color: #F2F2F2; + cursor: move; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + border-radius: 3px; + padding-left: 0.5em; } + .imageManager .imageMapper .draggableItem .text { + display: inline-block; + color: #4e4e4e; + padding-left: 0.5em; + position: relative; + top: 50%; + left: 0%; + transform: translateY(-50%) translateX(0%); } + .imageManager .imageMapper .draggableItem .text > span { + font-size: 0.8em; + color: rgba(78, 78, 78, 0.7); } + .imageManager .imageMapper .draggableItem .thumbnail { + opacity: 0.5; + display: inline-block; + width: 1.5em; + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; } + .imageManager .imageMapper .draggableItem .thumbnail:hover { + opacity: 1; + cursor: pointer; } +.imageManager .imageMapper .panelBar { + background: #FFFFFF; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 2px solid #F2F2F2; + height: 40px; } + .imageManager .imageMapper .panelBar .title { + padding-left: 1em; + text-transform: uppercase; + color: #4e4e4e; + font-weight: bold; } + .imageManager .imageMapper .panelBar .action { + padding-right: 0.5em; } +.imageManager .imageMapper .topPanel { + flex-grow: 2; + display: flex; + flex-direction: column; + width: 97%; + height: 40vh; + margin-top: 1em; + margin-left: 1em; + background: #eaeaea; + color: #4e4e4e; } + .imageManager .imageMapper .topPanel > div { + width: 100%; + height: 100%; + margin: 0em; } + .imageManager .imageMapper .topPanel .panelBarGroup { + height: 50px; + display: flex; } + .imageManager .imageMapper .topPanel .boards { + display: flex; + width: 100%; + overflow-y: auto; } + .imageManager .imageMapper .topPanel .boards > div { + width: 50%; } + .imageManager .imageMapper .topPanel .binText { + position: relative; + top: 50%; + left: 50%; + transform: translateY(-50%) translateX(-50%); + text-transform: uppercase; + text-align: center; } +.imageManager .imageMapper .bottomPanel { + flex-grow: 2; + display: flex; + justify-content: space-between; + width: 97%; + margin-top: 1em; + margin-left: 1em; } + .imageManager .imageMapper .bottomPanel .backlog, .imageManager .imageMapper .bottomPanel .sideBacklog, .imageManager .imageMapper .bottomPanel .imageBacklog { + width: 49%; + padding: 0em; } + .imageManager .imageMapper .bottomPanel .backlog .scrollable, .imageManager .imageMapper .bottomPanel .sideBacklog .scrollable, .imageManager .imageMapper .bottomPanel .imageBacklog .scrollable { + overflow-y: auto; } + .imageManager .imageMapper .bottomPanel .sideBacklog .scrollable { + text-align: left; + height: 32vh; } + .imageManager .imageMapper .bottomPanel .imageBacklog { + height: 37vh; } + .imageManager .imageMapper .bottomPanel .imageBacklog .manifestSelection { + height: 40px; + padding: 0em 1em; + display: flex; + justify-content: space-evenly; + border-bottom: 2px solid #F2F2F2; + background: #FFFFFF; } + .imageManager .imageMapper .bottomPanel .imageBacklog .manifestSelection .title { + width: 70px; + padding-top: 10px; + padding-right: 10px; + color: #4e4e4e; } + .imageManager .imageMapper .bottomPanel .imageBacklog .manifestSelection .form { + flex-grow: 1; } + .imageManager .imageMapper .bottomPanel .imageBacklog .scrollable { + height: 27vh; } +.imageManager .imageMapper .mainToolbar { + position: fixed; + bottom: 0; + width: 100%; + height: 50px; + display: flex; + align-items: center; + background: #FFFFFF; + -webkit-box-shadow: 0px -2px 2px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0px -2px 2px 0px rgba(0, 0, 0, 0.05); } + .imageManager .imageMapper .mainToolbar .message { + padding-left: 1em; + text-transform: uppercase; + color: #4e4e4e; + font-size: 0.9em; } + .imageManager .imageMapper .mainToolbar .actions { + text-align: right; } + .imageManager .imageMapper .mainToolbar > div { + width: 40%; } + +.addDialog .title { + color: #4e4e4e; } +.addDialog h3 { + border-bottom: 1px solid #ddd; } +.addDialog h4 { + color: #4e4e4e; + margin-top: 2em; + margin-bottom: 0em; + font-weight: 600; } +.addDialog .label { + width: 200px; + display: inline-block; } +.addDialog .input { + width: 200px; + display: inline-block; + text-align: right; } + +.feedbackDialog .label { + width: 100px; + display: inline-block; + vertical-align: top; } +.feedbackDialog .input { + width: 200px; + display: inline-block; + text-align: right; } + +.newProjectDialog h1 { + font-weight: normal; + text-transform: inherit; } +.newProjectDialog .newProjectSelection .selectItem { + display: flex; + padding: 1.9em 0em; + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; + border: 2px solid #FFFFFF; } + .newProjectDialog .newProjectSelection .selectItem:hover { + border: 2px solid #4ED6CB; + cursor: pointer; } + .newProjectDialog .newProjectSelection .selectItem .icon { + width: 50px; + padding: 0px 15px; } + .newProjectDialog .newProjectSelection .selectItem .text h1 { + font-size: 2em; + margin: 0; } + .newProjectDialog .newProjectSelection .selectItem .text h2 { + font-size: 1em; + color: #8e8e8e; + padding-top: 5px; + margin: 0; } + +.tooltip { + position: relative; + display: inline-block; + width: 100%; } + .tooltip .text { + visibility: hidden; + width: 210px; + font-weight: 300; + background: rgba(40, 40, 40, 0.9); + color: #fff; + text-align: center; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -ms-border-radius: 6px; + border-radius: 6px; + padding: 0.5em; + margin: 1em; + font-size: 0.9em; + opacity: 0; + -webkit-transition: all 200ms ease-in-out; + -ms-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; + /* Position the tooltip */ + position: absolute; + z-index: 100; } + .tooltip .text::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 rgba(40, 40, 40, 0.9) transparent; } + .tooltip.addDialog .text { + width: 70%; } + .tooltip.addDialog .text.active { + visibility: visible; + opacity: 1; } + .tooltip.addDialog .text::after { + left: 20%; } + .tooltip.eyeToggle { + width: initial; } + .tooltip.eyeToggle .text { + left: -5%; + margin-left: 0; + width: 100px; } + .tooltip.eyeToggle .text::after { + bottom: 100%; + /* At the top of the tooltip */ + left: 15%; + margin-left: -5px; } + .tooltip.eyeToggle .text.active { + visibility: visible; + opacity: 1; } + +h1 { + font-size: 1.5em; + text-transform: uppercase; + font-weight: bold; + color: #4e4e4e; } + +h2 { + font-weight: normal; + color: #4e4e4e; + padding-top: 0.8em; } + +html, body { + background: #F2F2F2; } + +/*# sourceMappingURL=App.css.map */ diff --git a/viscoll-app/src/styles/App.css.map b/viscoll-app/src/styles/App.css.map new file mode 100644 index 00000000..ea3e09fd --- /dev/null +++ b/viscoll-app/src/styles/App.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "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", +"sources": ["../../sass/layout/_landing.scss","../../sass/lib/_variables.scss","../../sass/layout/_sidebar.scss","../../sass/lib/_mixins.scss","../../sass/layout/_projectPanel.scss","../../sass/layout/_infobox.scss","../../sass/layout/_workspace.scss","../../sass/layout/_tabular.scss","../../sass/layout/_topbar.scss","../../sass/layout/_notes.scss","../../sass/layout/_filter.scss","../../sass/layout/_loading.scss","../../sass/layout/_404.scss","../../sass/layout/_imageManager.scss","../../sass/components/_dialog.scss","../../sass/components/_tooltip.scss","../../sass/typography.scss","../../sass/index.scss"], +"names": [], +"file": "App.css" +} \ No newline at end of file diff --git a/viscoll-app/src/styles/button.js b/viscoll-app/src/styles/button.js new file mode 100644 index 00000000..bf8114c5 --- /dev/null +++ b/viscoll-app/src/styles/button.js @@ -0,0 +1,27 @@ +export let btnLg = { + buttonStyle: { + height: 60, + }, + labelStyle: { + fontSize: 20, + }, + overlayStyle: { + paddingTop: 12, + height: 48, + } +} + + +export let btnMd = { + buttonStyle: { + height: 50, + }, + labelStyle: { + fontSize: 18, + }, + overlayStyle: { + paddingTop: 8, + height: 42, + } +} + diff --git a/viscoll-app/src/styles/index.css b/viscoll-app/src/styles/index.css new file mode 100644 index 00000000..b4cc7250 --- /dev/null +++ b/viscoll-app/src/styles/index.css @@ -0,0 +1,5 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} diff --git a/viscoll-app/src/styles/infobox.js b/viscoll-app/src/styles/infobox.js new file mode 100644 index 00000000..e25c7a2e --- /dev/null +++ b/viscoll-app/src/styles/infobox.js @@ -0,0 +1,6 @@ +let infoBoxStyle = { + tab: { + color: '#6A6A6A' + }, +} +export default infoBoxStyle; \ No newline at end of file diff --git a/viscoll-app/src/styles/light.js b/viscoll-app/src/styles/light.js new file mode 100644 index 00000000..029420db --- /dev/null +++ b/viscoll-app/src/styles/light.js @@ -0,0 +1,41 @@ +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _colors = require('material-ui/styles/colors'); + +var _colorManipulator = require('material-ui/utils/colorManipulator'); + +var _spacing = require('material-ui/styles/spacing'); + +var _spacing2 = _interopRequireDefault(_spacing); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = { + spacing: _spacing2.default, + fontFamily: 'Roboto, sans-serif', + borderRadius: 2, + palette: { + primary1Color: '#526C91', + primary2Color: '#3A4B55', + primary3Color: _colors.grey400, + accent1Color: '#4ED6CB', + accent2Color: _colors.grey100, + accent3Color: _colors.grey500, + textColor: "#4e4e4e", + secondaryTextColor: (0, _colorManipulator.fade)(_colors.darkBlack, 0.54), + alternateTextColor: _colors.white, + canvasColor: _colors.white, + borderColor: _colors.grey300, + disabledColor: (0, _colorManipulator.fade)(_colors.darkBlack, 0.3), + pickerHeaderColor: _colors.cyan500, + clockCircleColor: (0, _colorManipulator.fade)(_colors.darkBlack, 0.07), + shadowColor: _colors.fullBlack + }, + tableRow: { + selectedColor: '#fff', + }, +}; /** + * NB: If you update this file, please also update `docs/src/app/customization/Themes.js` + */ \ No newline at end of file diff --git a/viscoll-app/src/styles/sidebar.js b/viscoll-app/src/styles/sidebar.js new file mode 100644 index 00000000..4bd8eada --- /dev/null +++ b/viscoll-app/src/styles/sidebar.js @@ -0,0 +1,22 @@ +import light from "./light.js"; + +let sidebarStyle = { + panel: { + main: { + background: light.palette.primary2Color, + boxShadow: "none", + + }, + title: { + textTransform: "uppercase", + fontSize: "1.1em" + }, + text: { + background: "rgba(82, 108, 145, 0.2)", + overflowY: "auto", + maxHeight: "40vh", + + } + }, +} +export default sidebarStyle; \ No newline at end of file diff --git a/viscoll-app/src/styles/tabular.js b/viscoll-app/src/styles/tabular.js new file mode 100644 index 00000000..959b6831 --- /dev/null +++ b/viscoll-app/src/styles/tabular.js @@ -0,0 +1,32 @@ +let tabularStyle = { + group: { + card: { + marginBottom: 10, + boxShadow: "0px 1px 2px 1px rgba(0,0,0,0.1)", + paddingLeft: 0, + border: "2px solid white" + }, + cardHeader: { + padding: 0, + overflow: "hidden", + }, + containerStyle: { + paddingBottom: 0, + paddingTop: 0 + }, + cardTextStyle: { + paddingTop: 0, + paddingBottom: 0 + } + }, + leaf: { + card: { + marginBottom: 5, + overflow: "hidden", + textOverflow: "ellipsis", + boxShadow: "0px 1px 1px 1px rgba(0,0,0,0.1)", + border: "2px solid white" + } + } +} +export default tabularStyle; \ No newline at end of file diff --git a/viscoll-app/src/styles/textfield.js b/viscoll-app/src/styles/textfield.js new file mode 100644 index 00000000..23f1d31f --- /dev/null +++ b/viscoll-app/src/styles/textfield.js @@ -0,0 +1,16 @@ +const floatFieldDark = { + floatingLabelStyle: { + color: "#8dacd8", + }, + underlineStyle: { + border: "1px solid #526C91", + }, + underlineFocusStyle: { + border: "1px solid #4ED6CB", + }, + inputStyle: { + color: "white", + } +} + +export default floatFieldDark; \ No newline at end of file diff --git a/viscoll-app/src/styles/topbar.js b/viscoll-app/src/styles/topbar.js new file mode 100644 index 00000000..79da87ec --- /dev/null +++ b/viscoll-app/src/styles/topbar.js @@ -0,0 +1,8 @@ +let topbarStyle = { + tab: { + width:200, + height: 55, + color: '#6A6A6A' + }, +} +export default topbarStyle; \ No newline at end of file diff --git a/viscoll-app/styleguide.config.js b/viscoll-app/styleguide.config.js new file mode 100644 index 00000000..a5745de2 --- /dev/null +++ b/viscoll-app/styleguide.config.js @@ -0,0 +1,54 @@ +module.exports = { + assetsDir: "docs/assets", + sections: [ + { + name: 'Introduction', + content: 'docs/introduction.md' + }, + { + name: 'Containers', + components: 'src/containers/*.js' + }, + { + name: 'Components', + components: 'src/components/*.js', + sections: [ + { + name: 'Authentication', + components: 'src/components/authentication/*.js' + }, + { + name: 'Dashboard', + components: 'src/components/dashboard/*.js' + }, + { + name: 'Topbar', + components: 'src/components/topbar/*.js' + }, + { + name: 'Project', + components: 'src/components/project/*.js' + }, + { + name: 'Info Box', + components: 'src/components/infoBox/*.js', + sections: [ + { + name: 'Dialog', + components: 'src/components/infoBox/dialog/*.js' + }, + ] + }, + { + name: 'Tabular Mode', + components: 'src/components/tabularMode/*.js' + }, + { + name: 'Custom', + components: 'src/components/custom/*.js' + }, + ] + }, + + ] +} \ No newline at end of file From 4bd734ffd70eae2fd0c2bb09dc6595c91f48413e Mon Sep 17 00:00:00 2001 From: Jana Rajakumar Date: Wed, 29 Nov 2017 13:17:11 -0500 Subject: [PATCH 02/20] Deploy VisColl 0.7.0 to master - DIY Image Upload. - Increase web accessibility. - End user documentation. --- viscoll-api/Gemfile | 5 +- viscoll-api/Gemfile.lock | 12 + .../app/controllers/export_controller.rb | 5 +- .../app/controllers/feedback_controller.rb | 14 +- .../app/controllers/groups_controller.rb | 2 +- .../app/controllers/import_controller.rb | 16 +- .../app/controllers/leafs_controller.rb | 36 +- .../app/controllers/notes_controller.rb | 49 +- .../controller_helper/export_helper.rb | 390 +- .../controller_helper/filter_helper.rb | 4 +- .../controller_helper/groups_helper.rb | 3 +- .../controller_helper/import_helper.rb | 255 +- .../helpers/controller_helper/leafs_helper.rb | 4 +- .../controller_helper/projects_helper.rb | 81 +- .../group_validation_helper.rb | 32 +- .../leaf_validation_helper.rb | 10 +- viscoll-api/app/mailers/feedback_mailer.rb | 6 +- viscoll-api/app/mailers/mailer.rb | 6 +- viscoll-api/app/models/group.rb | 3 +- viscoll-api/app/models/note.rb | 16 +- viscoll-api/app/models/project.rb | 19 +- viscoll-api/app/models/side.rb | 3 +- .../app/views/exports/show.json.jbuilder | 5 - .../feedback_mailer/sendFeedback.html.erb | 24 +- .../config/initializers/rails_jwt_auth.rb | 1 + viscoll-api/public/viscoll-datamodel2.rng | 296 ++ viscoll-api/spec/fixtures/uoft_hollar.json | 1 + .../spec/fixtures/villanova_boston.json | 93 + .../controller_helper/export_helper_spec.rb | 76 +- .../controller_helper/filter_helper_spec.rb | 189 +- .../controller_helper/groups_helper_spec.rb | 32 +- .../controller_helper/import_helper_spec.rb | 109 +- .../controller_helper/leafs_helper_spec.rb | 162 +- .../controller_helper/projects_helper_spec.rb | 126 +- .../group_validation_helper_spec.rb | 193 +- .../leaf_validation_helper_spec.rb | 152 +- .../project_validation_helper_spec.rb | 79 +- viscoll-api/spec/mailers/feedback_spec.rb | 2 +- viscoll-api/spec/models/group_spec.rb | 180 +- viscoll-api/spec/models/leaf_spec.rb | 102 +- viscoll-api/spec/models/note_spec.rb | 124 +- viscoll-api/spec/models/project_spec.rb | 63 + viscoll-api/spec/models/side_spec.rb | 46 +- .../requests/feedback/create_feedback_spec.rb | 21 + .../spec/requests/leafs/leafs_conjoin_spec.rb | 185 + .../spec/requests/leafs/leafs_create_spec.rb | 161 + .../leafs/leafs_destroy_multiple_spec.rb | 179 + .../spec/requests/leafs/leafs_destroy_spec.rb | 167 + .../leafs/leafs_update_multiple_spec.rb | 199 + .../spec/requests/leafs/leafs_update_spec.rb | 151 + .../spec/requests/notes/notes_create_spec.rb | 33 + .../requests/notes/notes_create_type_spec.rb | 18 + .../requests/notes/notes_delete_type_spec.rb | 18 + .../requests/notes/notes_update_type_spec.rb | 18 + .../projects/create_manifest_projects_spec.rb | 133 + .../projects/delete_manifest_projects_spec.rb | 161 + .../projects/update_manifest_projects_spec.rb | 180 + viscoll-api/spec/spec_helper.rb | 7 +- viscoll-app/package-lock.json | 4252 ++++++++++------- viscoll-app/package.json | 6 +- viscoll-app/sass/components/_dialog.scss | 59 +- viscoll-app/sass/components/_textarea.scss | 8 + viscoll-app/sass/index.scss | 2 + viscoll-app/sass/layout/_dashboard.scss | 46 + viscoll-app/sass/layout/_imageManager.scss | 46 +- viscoll-app/sass/layout/_infobox.scss | 12 + viscoll-app/sass/layout/_notes.scss | 26 +- viscoll-app/sass/layout/_sidebar.scss | 52 +- viscoll-app/sass/layout/_tabular.scss | 119 +- viscoll-app/sass/lib/_variables.scss | 2 +- .../editCollation/interactionActions.js | 22 +- .../editCollation/modificationActions.js | 23 +- viscoll-app/src/actions/userActions.js | 4 +- .../src/assets/visualMode/PaperLeaf.js | 150 +- .../src/assets/visualMode/PaperManager.js | 143 +- .../src/components/authentication/Login.js | 27 +- .../src/components/authentication/Register.js | 50 +- .../authentication/ResendConfirmation.js | 28 +- .../authentication/ResetPassword.js | 7 +- .../authentication/ResetPasswordRequest.js | 32 +- .../collationManager/TabularMode.js | 82 +- .../collationManager/ViewingMode.js | 36 +- .../components/collationManager/VisualMode.js | 25 +- .../collationManager/dialog/NoteDialog.js | 113 + .../collationManager/tabularMode/Group.js | 228 +- .../collationManager/tabularMode/Leaf.js | 45 +- .../collationManager/tabularMode/Side.js | 42 +- .../src/components/dashboard/CloneProject.js | 89 +- .../components/dashboard/EditProjectForm.js | 57 +- .../src/components/dashboard/ImportProject.js | 119 +- .../src/components/dashboard/ListView.js | 50 +- .../components/dashboard/NewProjectChoice.js | 26 + .../dashboard/NewProjectContainer.js | 47 +- .../dashboard/NewProjectSelection.js | 111 +- .../components/dashboard/ProjectDetails.js | 17 +- .../components/dashboard/ProjectStructure.js | 96 +- viscoll-app/src/components/export/Export.js | 1 + .../src/components/filter/FilterRow.js | 31 +- .../src/components/global/PageNotFound.js | 2 +- viscoll-app/src/components/global/Panel.js | 55 +- .../components/imageManager/AddManifest.js | 5 +- .../components/imageManager/DeleteManifest.js | 3 +- .../components/imageManager/EditManifest.js | 6 +- .../imageManager/ManageManifests.js | 31 +- .../src/components/imageManager/MapImages.js | 745 ++- .../imageManager/mapImages/ImageBacklog.js | 76 + .../imageManager/mapImages/MapBoard.js | 163 + .../imageManager/mapImages/SideBacklog.js | 69 + .../src/components/infoBox/GroupInfoBox.js | 225 +- .../src/components/infoBox/LeafInfoBox.js | 97 +- .../src/components/infoBox/SideInfoBox.js | 81 +- .../infoBox/dialog/AddGroupDialog.js | 69 +- .../infoBox/dialog/AddLeafDialog.js | 48 +- .../src/components/infoBox/dialog/AddNote.js | 36 +- .../dialog/DeleteConfirmationDialog.js | 18 +- .../infoBox/dialog/VisualizationDialog.js | 146 + .../notesManager/DeleteConfirmation.js | 25 +- .../components/notesManager/EditNoteForm.js | 42 +- .../components/notesManager/ManageNotes.js | 46 +- .../components/notesManager/NewNoteForm.js | 42 +- .../src/components/notesManager/NoteType.js | 16 +- .../components/notesManager/NotesFilter.js | 10 + .../src/components/topbar/UserProfileForm.js | 68 +- viscoll-app/src/containers/Authentication.js | 13 +- .../src/containers/CollationManager.js | 377 +- viscoll-app/src/containers/Dashboard.js | 30 +- viscoll-app/src/containers/Feedback.js | 77 +- viscoll-app/src/containers/Filter.js | 33 +- viscoll-app/src/containers/ImageManager.js | 162 +- viscoll-app/src/containers/InfoBox.js | 166 +- viscoll-app/src/containers/NotesManager.js | 40 +- viscoll-app/src/containers/Project.js | 19 +- viscoll-app/src/containers/TopBar.js | 55 +- .../src/reducers/editCollationReducer.js | 26 +- .../src/reducers/initialStates/active.js | 9 +- viscoll-app/src/styles/App.css | 341 +- viscoll-app/src/styles/App.css.map | 4 +- viscoll-app/src/styles/button.js | 10 + viscoll-app/src/styles/textfield.js | 12 +- 139 files changed, 10229 insertions(+), 4257 deletions(-) create mode 100644 viscoll-api/public/viscoll-datamodel2.rng create mode 100644 viscoll-api/spec/fixtures/uoft_hollar.json create mode 100644 viscoll-api/spec/fixtures/villanova_boston.json create mode 100644 viscoll-api/spec/models/project_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_create_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb create mode 100644 viscoll-api/spec/requests/leafs/leafs_update_spec.rb create mode 100644 viscoll-api/spec/requests/projects/create_manifest_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb create mode 100644 viscoll-app/sass/components/_textarea.scss create mode 100644 viscoll-app/sass/layout/_dashboard.scss create mode 100644 viscoll-app/src/components/collationManager/dialog/NoteDialog.js create mode 100644 viscoll-app/src/components/dashboard/NewProjectChoice.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/ImageBacklog.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/MapBoard.js create mode 100644 viscoll-app/src/components/imageManager/mapImages/SideBacklog.js create mode 100644 viscoll-app/src/components/infoBox/dialog/VisualizationDialog.js diff --git a/viscoll-api/Gemfile b/viscoll-api/Gemfile index a369fca9..b88617fa 100644 --- a/viscoll-api/Gemfile +++ b/viscoll-api/Gemfile @@ -32,6 +32,7 @@ group :development, :test do 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 @@ -49,7 +50,3 @@ gem 'rails_jwt_auth', '~> 0.16.1' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible gem 'rack-cors', '~> 0.4.1' - - - - diff --git a/viscoll-api/Gemfile.lock b/viscoll-api/Gemfile.lock index ca4c3ee7..df3c02ad 100644 --- a/viscoll-api/Gemfile.lock +++ b/viscoll-api/Gemfile.lock @@ -47,6 +47,8 @@ GEM i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) bcrypt (3.1.11) bson (4.2.1) @@ -54,6 +56,8 @@ GEM byebug (9.0.6) coderay (1.1.1) concurrent-ruby (1.0.5) + crack (0.4.3) + safe_yaml (~> 1.0.0) database_cleaner (1.6.1) diff-lcs (1.3) docile (1.1.5) @@ -83,6 +87,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) + hashdiff (0.3.7) i18n (0.8.4) jbuilder (2.7.0) activesupport (>= 4.2.0) @@ -120,6 +125,7 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) + public_suffix (3.0.1) puma (3.9.1) rack (2.0.3) rack-cors (0.4.1) @@ -180,6 +186,7 @@ GEM rspec-support (3.6.0) rspec_junit_formatter (0.3.0) rspec-core (>= 2, < 4, != 2.12.0) + safe_yaml (1.0.4) shellany (0.0.1) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) @@ -207,6 +214,10 @@ GEM thread_safe (~> 0.1) warden (1.2.7) rack (>= 1.0) + webmock (3.1.0) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -235,6 +246,7 @@ DEPENDENCIES spring spring-watcher-listen (~> 2.0.0) tzinfo-data + webmock (~> 3.1.0) BUNDLED WITH 1.14.6 diff --git a/viscoll-api/app/controllers/export_controller.rb b/viscoll-api/app/controllers/export_controller.rb index 8ab80312..77a3c6c8 100644 --- a/viscoll-api/app/controllers/export_controller.rb +++ b/viscoll-api/app/controllers/export_controller.rb @@ -9,14 +9,11 @@ def show when "xml" exportData = buildDotModel(@project) render json: {data: exportData, type: @format}, status: :ok - when "formula" - exportData = buildFormula(@project) - render json: {data: exportData, type: @format}, status: :ok when "json" @data = buildJSON(@project) render :'exports/show', status: :ok else - render json: {error: "Export format must be one of [json, xml, formula]"}, status: :unprocessable_entity + render json: {error: "Export format must be one of [json, xml]"}, status: :unprocessable_entity end rescue Exception => e render json: {error: e.message}, status: :internal_server_error diff --git a/viscoll-api/app/controllers/feedback_controller.rb b/viscoll-api/app/controllers/feedback_controller.rb index b83ad9ea..7e942845 100644 --- a/viscoll-api/app/controllers/feedback_controller.rb +++ b/viscoll-api/app/controllers/feedback_controller.rb @@ -4,17 +4,25 @@ class FeedbackController < ApplicationController # POST /feedback def create begin + if not current_user + render json: {}, status: :unprocessable_entity + end @title = feedback_params[:title] @message = feedback_params[:message] - if not @title or not @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 return end FeedbackMailer.sendFeedback( @title, @message, + @browserInformation, + @projectJSONExport, current_user ).deliver_now + render json: {}, status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -22,6 +30,6 @@ def create private def feedback_params - params.require(:feedback).permit(:title, :message) + params.require(:feedback).permit(:title, :message, :browserInformation, :project) end -end \ No newline at end of file +end diff --git a/viscoll-api/app/controllers/groups_controller.rb b/viscoll-api/app/controllers/groups_controller.rb index f10ed902..e79ca1a9 100644 --- a/viscoll-api/app/controllers/groups_controller.rb +++ b/viscoll-api/app/controllers/groups_controller.rb @@ -181,7 +181,7 @@ def set_group end def group_params - params.require(:group).permit(:project_id, :type, :title, :tacketed) + params.require(:group).permit(:project_id, :type, :title, :tacketed=>[], :sewing=>[]) end def additional_params diff --git a/viscoll-api/app/controllers/import_controller.rb b/viscoll-api/app/controllers/import_controller.rb index 5e1faf5f..27511670 100644 --- a/viscoll-api/app/controllers/import_controller.rb +++ b/viscoll-api/app/controllers/import_controller.rb @@ -7,15 +7,19 @@ def index importData = imported_data.to_h[:importData] importFormat = imported_data.to_h[:importFormat] begin - # Skip all callbacks - Leaf.skip_callback(:create, :after, :create_sides) case importFormat when "json" handleJSONImport(JSON.parse(importData)) when "xml" - # handleXMLImport(Hash.from_xml(importData)) - when "formula" - + xml = Nokogiri::XML(importData) + schema = Nokogiri::XML::RelaxNG(File.open("public/viscoll-datamodel2.rng")) + errors = schema.validate(xml) + if errors.empty? + handleXMLImport(Hash.from_xml(importData)["viscoll"]["manuscript"], xml) + else + render json: {error: errors}, status: :unprocessable_entity + return + end end # render json: {error: "RETURING ERROR FOR NOW"}, status: :unprocessable_entity @projects = current_user.projects.order_by(:updated_at => 'desc') @@ -23,8 +27,6 @@ def index rescue Exception => e render json: {error: errorMessage}, status: :unprocessable_entity ensure - # Add all callbacks again - Leaf.set_callback(:create, :after, :create_sides) end end diff --git a/viscoll-api/app/controllers/leafs_controller.rb b/viscoll-api/app/controllers/leafs_controller.rb index ae69c356..c4c5d0b1 100644 --- a/viscoll-api/app/controllers/leafs_controller.rb +++ b/viscoll-api/app/controllers/leafs_controller.rb @@ -30,6 +30,13 @@ def create render json: {additional: @additionalErrors}, status: :unprocessable_entity 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 + return + end newlyAddedLeafIDs = [] newlyAddedLeafs = [] @@ -67,7 +74,6 @@ def create @group.add_members(newlyAddedLeafIDs, memberOrder) # SUCCESS - @project = Project.find(project_id) @data = generateResponse() render :'projects/show', status: :ok end @@ -95,12 +101,22 @@ def update def updateMultiple begin allLeafs = leaf_params_batch_update.to_h[:leafs] - @project = Project.find(leaf_params_batch_update.to_h[:project_id]) + 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 + 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 + return + end + if @leaf.project.user_id != current_user.id + render json: {error: ""}, status: :unauthorized + return end if !@leaf.update(leaf_params[:attributes]) render json: {leafs: {attributes: {index: @leaf.errors}}}, status: :unprocessable_entity @@ -162,7 +178,11 @@ def destroyMultiple @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 + return + end + # Detach its conjoined leaf if any if leaf.conjoined_to @project.leafs.find(leaf.conjoined_to).update(conjoined_to: nil) @@ -206,16 +226,22 @@ def conjoinLeafs # VALIDATION ERRORS @errors = [] haveErrors = false + allowed_project_ids = current_user.projects.pluck(:id).collect { |pid| pid.to_s } leafIDs.each do |leafID| begin - leaves.push(Leaf.find(leafID)) + leaf = Leaf.find(leafID) + if not allowed_project_ids.include?(leaf.project_id.to_s) + render json: {error: ""}, status: :unauthorized + 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 = "Minimum of 2 leaves required to conjoin" + @errors.push("Minimum of 2 leaves required to conjoin") haveErrors = true end if haveErrors diff --git a/viscoll-api/app/controllers/notes_controller.rb b/viscoll-api/app/controllers/notes_controller.rb index e6ff5d94..25f11531 100644 --- a/viscoll-api/app/controllers/notes_controller.rb +++ b/viscoll-api/app/controllers/notes_controller.rb @@ -1,17 +1,27 @@ 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 + return + end + if @project.user != current_user + render json: {error: ''}, status: :unauthorized + return + end if @note.save if not Project.find(@note.project_id).noteTypes.include?(@note.type) render json: {type: "should be one of " +Project.find(@note.project_id).noteTypes.to_s}, status: :unprocessable_entity @note.delete return end - @project = Project.find(@note.project_id) @data = generateResponse() render :'projects/show', status: :ok else @@ -135,13 +145,6 @@ def unlink # POST /notes/type def createType type = note_type_params.to_h[:type] - project_id = note_type_params.to_h[:project_id] - begin - @project = Project.find(project_id) - rescue Mongoid::Errors::DocumentNotFound - render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity - return - end if @project.noteTypes.include?(type) render json: {type: type+" type already exists in the project"}, status: :unprocessable_entity return @@ -157,13 +160,6 @@ def createType # DELETE /notes/type def deleteType type = note_type_params.to_h[:type] - project_id = note_type_params.to_h[:project_id] - begin - @project = Project.find(project_id) - rescue Mongoid::Errors::DocumentNotFound - render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity - return - end if not @project.noteTypes.include?(type) render json: {type: type+" type doesn't exist in the project"}, status: :unprocessable_entity return @@ -184,13 +180,6 @@ def deleteType def updateType old_type = note_type_params.to_h[:old_type] type = note_type_params.to_h[:type] - project_id = note_type_params.to_h[:project_id] - begin - @project = Project.find(project_id) - rescue Mongoid::Errors::DocumentNotFound - render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity - return - end if not @project.noteTypes.include?(old_type) render json: {old_type: old_type+" type doesn't exist in the project"}, status: :unprocessable_entity return @@ -228,10 +217,24 @@ def set_note render json: {error: e.message}, status: :unprocessable_entity 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 + return + end + rescue Mongoid::Errors::DocumentNotFound + render json: {project_id: "project not found with id "+project_id}, status: :unprocessable_entity + 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, :title, :type, :description) + params.require(:note).permit(:project_id, :title, :type, :description, :show) end def note_update_params diff --git a/viscoll-api/app/helpers/controller_helper/export_helper.rb b/viscoll-api/app/helpers/controller_helper/export_helper.rb index 7cccbe7c..93565328 100644 --- a/viscoll-api/app/helpers/controller_helper/export_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/export_helper.rb @@ -26,114 +26,116 @@ def buildJSON(project) rootMemberOrder = 1 @groupIDs.each_with_index do | groupID, index| group = @project.groups.find(groupID) - @groups[group.id.to_s] = { - "order": index + 1, - "type": group.type, - "title": group.title, + @groups[index + 1] = { + "params": { + "type": group.type, + "title": group.title, + "nestLevel": group.nestLevel + }, "tacketed": group.tacketed, - "nestLevel": group.nestLevel, - "parentID": group.parentID, - "notes": [], - "memberOrders": group.memberIDs, - "memberType": "Group", - "memberOrder": group.parentID ? nil : rootMemberOrder + "sewing": group.sewing, + "parentOrder": group.parentID, + "memberOrders": group.memberIDs } if group.nestLevel == 1 rootMemberOrder += 1 end end - # Generate @leafIDs - @groups.each do | groupID, group | - if group[:nestLevel] == 1 - getLeafMembers(group[:memberOrders]) + # Generate @leafIDs list + @groups.each do | groupOrder, group | + if group[:params][:nestLevel] == 1 + getLeafMemberOrders(group[:memberOrders]) end end - @project.leafs.each_with_index do | leaf, index | - @leafs[leaf.id.to_s] = { - "order": index + 1, - "material": leaf.material, - "type": leaf.type, - "attachment_method": leaf.attachment_method, + @leafIDs.each_with_index do | leafID, index | + leaf = @project.leafs.find(leafID) + @leafs[index + 1] = { + "params": { + "material": leaf.material, + "type": leaf.type, + "attachment_method": leaf.attachment_method, + "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, - "attached_above": leaf.attached_above, - "attached_below": leaf.attached_below, - "stub": leaf.stub, - "nestLevel": leaf.nestLevel, - "parentOrder": @groups[leaf.parentID][:order], - "rectoOrder": leaf.rectoID, - "versoOrder": leaf.versoID, - "notes": [], + "parentOrder": @groupIDs.index(leaf.parentID)+1, + "rectoOrder": index + 1, + "versoOrder": index + 1, } + @rectoIDs.push(leaf.rectoID) + @versoIDs.push(leaf.versoID) end - @leafIDs.each do | leafID | - leaf = @leafs[leafID] - @rectoIDs.push(leaf[:rectoOrder]) - @versoIDs.push(leaf[:versoOrder]) - end - - # Transform leaf recto and verso IDs to orders - @leafs.each do | leafID, leaf | - leaf[:rectoOrder] = @rectoIDs.index(leaf[:rectoOrder])+1 - leaf[:versoOrder] = @versoIDs.index(leaf[:versoOrder])+1 - end - - - # Transform group.memberOrders to member global order and group.tacketed to leaf order + # 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_"+@groups[memberID][:order].to_s) + memberOrders.push("Group_" + (@groupIDs.index(memberID)+1).to_s) else - memberOrders.push("Leaf_"+@leafs[memberID][:order].to_s) + memberOrders.push("Leaf_" + (@leafIDs.index(memberID)+1).to_s) end end group[:memberOrders] = memberOrders - if group[:tacketed] != "" - group[:tacketed] = @leafs[group[:tacketed]][:order] - end + 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 - @project.sides.each_with_index do | side, index | - parentOrder = @leafIDs.index(side.parentID) + 1 - obj = { - "order": index + 1, - "parentOrder": parentOrder, - "folio_number": side.folio_number ? side.folio_number : parentOrder.to_s + side.id[0], - "texture": side.texture, - "image": side.image, - "script_direction": side.script_direction, + @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 : parentOrder.to_s + recto.id[0], + "texture": recto.texture, + "image": recto.image, + "script_direction": recto.script_direction + }, + "parentOrder": parentOrder } - if side.id[0] == "R" - @rectos[side.id.to_s] = obj - elsif side.id[0] == "V" - @versos[side.id.to_s] = obj - end end - @project.notes.each do | note | - @notes[note.id.to_s] = { - "title": note.title, - "type": note.type, - "description": note.description, - "show": note.show, + @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 : parentOrder.to_s + verso.id[0], + "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[note.id.to_s][:objects][:Group] = note.objects["Group"].map { |groupID| @groups[groupID][:order] } - @notes[note.id.to_s][:objects][:Leaf] = note.objects["Leaf"].map { |leafID| @leafs[leafID][:order]} - @notes[note.id.to_s][:objects][:Recto] = note.objects["Recto"].map { |rectoID| @rectos[rectoID][:order]} - @notes[note.id.to_s][:objects][:Verso] = note.objects["Verso"].map { |versoID| @versos[versoID][:order]} + @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, - "groupIDs": @groupIDs, - "leafIDs": @leafIDs, - "rectoIDs": @rectoIDs, - "versoIDs": @versoIDs, "groups": @groups, "leafs": @leafs, "rectos": @rectos, @@ -144,56 +146,234 @@ def buildJSON(project) # Populate leaf orders recursively - def getLeafMembers(memberIDs) + def getLeafMemberOrders(memberIDs) memberIDs.each_with_index do | memberID, index | if memberID[0] == "G" - getLeafMembers(@groups[memberID][:memberIDs]) - @groups[memberID][:memberOrder] = index + 1 + getLeafMemberOrders(@groups[@groupIDs.index(memberID)+1][:memberOrders]) elsif memberID[0] == "L" @leafIDs.push(memberID) - @leafs[memberID] = {"memberOrder": index + 1} end end end + + + def buildDotModel(project) - xml = Nokogiri::XML::Builder.new { |xml| - xml.manuscript do - xml.title project.title - xml.shelfmark project.shelfmark - project.groups.each_with_index do |group| - xml.quire :n => group.order, :level => group.getNestLevel do - leafIndex = 1 - group.get_members.each do |member| - if member[:type]=="Leaf" - leaf = Leaf.find(member[:id]) - n = leafIndex - mode = leaf.type - conjoinedLeaf = project.leafs.find(leaf.conjoined_to) - single = conjoinedLeaf.order < 1 - conjoin = "" - if not single - conjoin = Grouping.find_by(member_id: conjoinedLeaf.id)[:order] + @groupIDs = project.groupIDs + @leafIDs = [] + @leafs = {} + @groups = {} + @rectos = {} + @versos = {} + return Nokogiri::XML::Builder.new { |xml| + xml.viscoll :xmlns => "http://schoenberginstitute.org/schema/collation" do + 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 = project.groups.find(groupID) + getLeafMemberIDs(group.memberIDs, project) + parents = parentsOrders(groupID, project) + groupMemberOrder = parents.pop + idPostfix = parents.empty? ? groupMemberOrder.to_s : parents.join("-")+"-"+groupMemberOrder.to_s + quireAttributes = {} + quireAttributes["xml:id"] = idPrefix+"-q-"+idPostfix + quireAttributes[:n] = index + 1 + quireAttributes[:certainty] = 1 + if group.parentID + quireAttributes[:parent] = idPrefix+"-q-"+(@groupIDs.index(group.parentID)+1).to_s + end + xml.quire quireAttributes do + xml.text index + 1 + end + @groups[groupID] = quireAttributes["xml:id"] + end + end + @leafIDs.each_with_index do |leafID, index| + leaf = project.leafs.find(leafID) + parents = parentsOrders(leafID, project) + leafMemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafMemberOrder.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 leaf.type != "None" + mode[:val] = leaf.type.downcase + mode[:certainty] = 1 + end + xml.mode mode + + qAttributes = {} + qAttributes[:position] = leafMemberOrder + qAttributes[:leafno] = leafMemberOrder + 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 + + if leaf.attached_above != "None" or leaf.attached_below != "None" + targetLeafAbove = parents.join("-")+"-"+(leafMemberOrder.to_i-1).to_s + targetLeafBelow = parents.join("-")+"-"+(leafMemberOrder.to_i+1).to_s + if leaf.attached_above != "None" and leaf.attached_below != "None" + xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafAbove+" #"+targetLeafBelow, :type => leaf.attached_above) do + xml.text leaf.attached_above + "_To_Above_and_Below" + end + elsif leaf.attached_above != "None" + xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafAbove, :type => leaf.attached_above) do + xml.text leaf.attached_above + "_To_Above" + end + elsif leaf.attached_below != "None" + xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafBelow, :type => leaf.attached_below) do + xml.text leaf.attached_below + "_To_Below" + end end - position = member[:order] - folio_number = leaf.sides[0].folio_number[/\d+/] - xml.leaf :n => n, :mode => mode, :single => single, :folio_number => folio_number, :conjoin => conjoin, :position => position - leafIndex += 1 + end + + rectoSide = project.sides.find(leaf.rectoID) + rectoAttributes = {} + rectoAttributes["xml:id"] = leafAttributes["xml:id"]+"-R" + rectoAttributes[:type] = "Recto" + if rectoSide.folio_number + rectoAttributes[:folioNumber] = rectoSide.folio_number + else + rectoAttributes[:folioNumber] = folioNumber[:val].to_s+"R" + 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 + + versoSide = project.sides.find(leaf.versoID) + versoAttributes = {} + versoAttributes["xml:id"] = leafAttributes["xml:id"]+"-V" + versoAttributes[:type] = "Verso" + if versoSide.folio_number + versoAttributes[:folioNumber] = versoSide.folio_number + else + versoAttributes[:folioNumber] = folioNumber[:val].to_s+"R" + 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 + end + @leafs[leafID]["xmlID"] = leafAttributes["xml:id"] + end + + project.notes.each_with_index do |note, index| + noteAttributes = {} + noteAttributes["xml:id"] = idPrefix+"-n-"+(index+1).to_s + noteAttributes[:type] = note.type + linkedObjectIDs = [] + note.objects["Group"].each do |groupID| + linkedObjectIDs.push("#"+@groups[groupID]) + end + note.objects["Leaf"].each do |leafID| + linkedObjectIDs.push("#"+@leafs[leafID]["xmlID"]) + end + note.objects["Recto"].each do |rectoID| + linkedObjectIDs.push("#"+@rectos[rectoID]["xml:id"]) + end + note.objects["Verso"].each do |versoID| + linkedObjectIDs.push("#"+@versos[versoID]["xml:id"]) + end + noteAttributes[:target] = linkedObjectIDs.join(" ") + xml.note noteAttributes do + xml.text note.title + ": " + note.description + end + end + + # @rectos.each do |rectoID, rectoAttributes| + # noteAttributes = {} + # noteAttributes["xml:id"] = rectoAttributes["xml:id"] + # noteAttributes[:target] = rectoAttributes[:target] + # noteAttributes[:type] = "Recto" + # # noteAttributes[:texture] = rectoAttributes[:texture] + # xml.note noteAttributes do + # xml.text rectoAttributes[:folioNumber] + # end + # end + + end + + xml.mapping do + @rectos.each do |rectoID, attributes| + if attributes[:image] + mapAttributes = {} + mapAttributes[:side] = attributes["xml:id"] + mapAttributes[:target] = attributes[:image] + xml.map mapAttributes do + termAttributes = {} + termAttributes[:target] = attributes[:image] + xml.term termAttributes end end end end + end }.to_xml - return xml end - def buildFormula(project) - result = "*4 x1 A-D12 E12 (E7 + 2x1) F-H12 I12 (I3 + 3x1) K-M12 N12 (N5 + 4x1) O-Q12" - return result + + # Populate leaf orders recursively + def getLeafMemberIDs(memberIDs, project, leafMember=1) + memberIDs.each_with_index do | memberID, index | + if memberID[0] == "G" + getLeafMemberIDs(project.groups.find(memberID).memberIDs, project, leafMember) + elsif memberID[0] == "L" + if not @leafIDs.include? memberID + @leafIDs.push(memberID) + @leafs[memberID] = {"memberOrder": leafMember} + 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 \ No newline at end of file +end + diff --git a/viscoll-api/app/helpers/controller_helper/filter_helper.rb b/viscoll-api/app/helpers/controller_helper/filter_helper.rb index 4eaf4432..7433e997 100644 --- a/viscoll-api/app/helpers/controller_helper/filter_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/filter_helper.rb @@ -52,7 +52,7 @@ def runValidations(queries) end when "folio_number", "uri" if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) - error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" + error["condition"] = "valid conditions for side attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" haveErrors = true end end @@ -96,4 +96,4 @@ def runValidations(queries) end end -end \ No newline at end of file +end diff --git a/viscoll-api/app/helpers/controller_helper/groups_helper.rb b/viscoll-api/app/helpers/controller_helper/groups_helper.rb index d574f702..7762cb5f 100644 --- a/viscoll-api/app/helpers/controller_helper/groups_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/groups_helper.rb @@ -1,5 +1,6 @@ module ControllerHelper module GroupsHelper + include ControllerHelper::LeafsHelper def addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) newlyAddedLeafs = [] @@ -19,4 +20,4 @@ def addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) end end -end \ No newline at end of file +end diff --git a/viscoll-api/app/helpers/controller_helper/import_helper.rb b/viscoll-api/app/helpers/controller_helper/import_helper.rb index b97ebf31..3381d65a 100644 --- a/viscoll-api/app/helpers/controller_helper/import_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/import_helper.rb @@ -3,6 +3,12 @@ module ImportHelper # JSON IMPORT def handleJSONImport(data) + # reference variables + allGroupsIDsInOrder = [] + allLeafsIDsInOrder = [] + allRectosIDsInOrder = [] + allVersosIDsInOrder = [] + # Create the Project begin Project.find_by(title: data["project"]["title"]) @@ -12,121 +18,200 @@ def handleJSONImport(data) data["project"]["user_id"] = current_user.id project = Project.create(data["project"]) - allLeafsInOrder = [] # Create all Leafs - data["leafs"].each do |leafID, leafParams| - leafParams["project_id"] = project.id - leaf = Leaf.create(leafParams) - allLeafsInOrder.push(leaf) + 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 - allGroupsInOrder = [] # Create all Groups - data["groups"].each do |groupID, groupParams| - if groupParams["tacketed"] != "" - groupParams["tacketed"] = allLeafsInOrder[groupParams["tacketed"]].id.to_s + 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 - groupParams["project_id"] = project.id - group = Group.create(groupParams["group"]) - allGroupsInOrder.push(group) + group.update(parentID: parentID, memberIDs: memberIDs) end - # Update all leafs with correct conjoinedTo leaf IDs - data["leafs"].each do |leafID, leafParams| - if leafParams[:conjoined_to] - leafConjoinedTo = allLeafsInOrder[leafParams[:conjoined_leaf_order]] - leaf.update(conjoined_to: leafConjoinedTo.id.to_s) + # 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 - # Create all Sides - sides = [] - data["sides"].each do |sideParams| - sideParams["side"]["leaf_id"] = project.leafs.find_by(order: sideParams["parentLeafOrder"]).id - side = Side.create(sideParams["side"]) - sides.push(side) + # 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 |noteParams| - noteParams["note"]["project_id"] = project.id - note = Note.create(noteParams["note"]) - # Generate objectIDs of Groups with this note + 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 = [] - noteParams["groupOrders"].each do |order| - group = project.groups.find_by(order: order) + data["objects"]["Group"].each do |groupOrder| + groupID = allGroupsIDsInOrder[groupOrder-1] + group = project.groups.find(groupID) group.notes.push(note) group.save - groupIDs.push(group.id.to_s) + groupIDs.push(groupID) end leafIDs = [] - noteParams["leafOrders"].each do |order| - leaf = project.leafs.find_by(order: order) + data["objects"]["Leaf"].each do |leafOrder| + leafID = allLeafsIDsInOrder[leafOrder-1] + leaf = project.leafs.find(leafID) leaf.notes.push(note) leaf.save - leafIDs.push(leaf.id.to_s) + leafIDs.push(leafID) end - sideIDs = [] - noteParams["sideOrders"].each do |order| - side = sides[order-1] - side.notes.push(note) - side.save - sideIDs.push(side.id.to_s) + 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 - note.objects["Group"] = groupIDs - note.objects["Leaf"] = leafIDs - note.objects["Side"] = sideIDs - end - - # Create all Groupings - data["groupings"].each do |groupingParams| - group = project.groups.find_by(order: groupingParams["groupOrder"]) - memberOrder = groupingParams["memberOrder"] - if (groupingParams["memberType"]=="Group") - newMember = project.groups.find_by(order: groupingParams["objectOrder"]) - group.add_members([newMember], memberOrder) - elsif (groupingParams["memberType"]=="Leaf") - newMember = project.leafs.find_by(order: groupingParams["objectOrder"]) - group.add_members([newMember], memberOrder) + 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) + 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 - # title = data["manuscript"]["title"] - # begin - # Project.find_by(title: title) - # title = "Copy of " + title + " @ " + Time.now.to_s - # rescue Exception => e - # end - # @project = Project.create(title: title, user_id: current_user.id) - - # # Create the Manuscript - # shelfmark = data["manuscript"]["shelfmark"] - # @project = Manuscript.create(shelfmark: shelfmark, project_id: @project.id) - - # # Create None & Binding Leafs - # Leaf.create({project_id: @project.id, order: -1}) - # Leaf.create({project_id: @project.id, order: 0}) - # # Create all Groups - # data["manuscript"]["quire"].each do |quire| - # p quire - # groupOrder = quire["n"] - # nestLevel = quire["level"] - # groupParams["group"]["project_id"] = @project.id - # @group = Group.create(project_id: @project.id, type: "Quire", order: groupOrder) - # # First, create all Leafs in this Group without attributes - # if (quire["leaf"]) - # quire["leaf"].each do |leaf| - # leafOrder = leaf["folio_number"] - # Leaf.create(project_id: @project.id, order: leafOrder) - # end - # end - # end + 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 diff --git a/viscoll-api/app/helpers/controller_helper/leafs_helper.rb b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb index 900728ad..b52e3a12 100644 --- a/viscoll-api/app/helpers/controller_helper/leafs_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb @@ -6,7 +6,7 @@ def autoConjoinLeaves(leaves, oddLeafNumber) if leaves.size.odd? oddLeaf = leaves[oddLeafNumber] if (oddLeaf.conjoined_to) - @project.leaves.find(oddLeaf.conjoined_to).update(conjoined_to: nil) + @project.leafs.find(oddLeaf.conjoined_to).update(conjoined_to: nil) oddLeaf.update(conjoined_to: nil) end leaves.delete_at(oddLeafNumber-1) @@ -74,4 +74,4 @@ def update_conjoined_partner(new_conjoined_to_leafID) end end end -end \ No newline at end of file +end diff --git a/viscoll-api/app/helpers/controller_helper/projects_helper.rb b/viscoll-api/app/helpers/controller_helper/projects_helper.rb index 900e4d9c..32ff55fb 100644 --- a/viscoll-api/app/helpers/controller_helper/projects_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/projects_helper.rb @@ -1,6 +1,7 @@ require 'net/http' module ControllerHelper module ProjectsHelper + include ControllerHelper::LeafsHelper def addGroupsLeafsConjoin(project, allGroups) groupIDs = [] allGroups.each do |groupInfo| @@ -66,7 +67,7 @@ def generateResponse() if manifestName.length>50 manifestName = manifestName[0,47] + "..." end - @projectInformation[:manifests][manifestID][:images] = manifestInformation[:images] + @projectInformation[:manifests][manifestID][:images] = manifestInformation[:images].map { |image| image.merge({manifestID: manifestID})} @projectInformation[:manifests][manifestID][:name] = manifestName end @@ -80,6 +81,7 @@ def generateResponse() "type": group.type, "title": group.title, "tacketed": group.tacketed, + "sewing": group.sewing, "nestLevel": group.nestLevel, "parentID": group.parentID, "notes": [], @@ -91,9 +93,9 @@ def generateResponse() rootMemberOrder += 1 end end - @groups.each do | group | - if group[1][:nestLevel] == 1 - getLeafMembers(group[1][:memberIDs]) + @groups.each do | groupID, group | + if group[:nestLevel] == 1 + getLeafMembers(group[:memberIDs]) end end @project.leafs.each do | leaf | @@ -118,11 +120,7 @@ def generateResponse() } end - @leafIDs.each do | leafID | - leaf = @leafs[leafID] - @rectoIDs.push(leaf[:rectoID]) - @versoIDs.push(leaf[:versoID]) - end + @project.sides.each do | side | parentOrder = @leafIDs.index(side.parentID) + 1 @@ -130,7 +128,7 @@ def generateResponse() "id": side.id.to_s, "parentID": side.parentID, "parentOrder": parentOrder, - "folio_number": side.folio_number ? side.folio_number : parentOrder.to_s + side.id[0], + "folio_number": side.folio_number, "texture": side.texture, "image": side.image, "script_direction": side.script_direction, @@ -144,6 +142,38 @@ def generateResponse() end end + # Generate list of recto and verso ID's + # Generate folio numbers for sides that do not have folio numbers parentOrder.to_s + side.id[0] + endleafCount = 0 + folioNumberCount = 0 + @leafIDs.each do | leafID | + leaf = @leafs[leafID] + @rectoIDs.push(leaf[:rectoID]) + @versoIDs.push(leaf[:versoID]) + recto = @rectos[leaf[:rectoID]] + verso = @versos[leaf[:versoID]] + if leaf[:type] == "Endleaf" + endleafCount += 1 + if recto[:folio_number] == nil + recto[:folio_number] = to_roman(endleafCount) + recto[:id][0] + end + if verso[:folio_number] == nil + verso[:folio_number] = to_roman(endleafCount) + verso[:id][0] + end + else + if (recto[:folio_number] == nil) || (verso[:folio_number] == nil) + folioNumberCount += 1 + end + if recto[:folio_number] == nil + recto[:folio_number] = (folioNumberCount).to_s + recto[:id][0] + end + if verso[:folio_number] == nil + verso[:folio_number] = (folioNumberCount).to_s + verso[:id][0] + end + end + + end + @project.notes.each do | note | @notes[note.id.to_s] = { "id": note.id.to_s, @@ -194,5 +224,34 @@ def getLeafMembers(memberIDs) end end + def roman_mapping + { + 1000 => "m", + 900 => "cm", + 500 => "d", + 400 => "cd", + 100 => "c", + 90 => "xc", + 50 => "l", + 40 => "xl", + 10 => "x", + 9 => "ix", + 5 => "v", + 4 => "iv", + 1 => "i" + } + end + + def to_roman(value) + result = "" + number = value + roman_mapping.keys.each do |divisor| + quotient, modulus = number.divmod(divisor) + result << roman_mapping[divisor] * quotient + number = modulus + end + result + end + end -end \ No newline at end of file +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 index ac3035cf..cbecf78d 100644 --- a/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb +++ b/viscoll-api/app/helpers/validation_helper/group_validation_helper.rb @@ -10,17 +10,13 @@ def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLe additionalErrors[:noOfGroups].push("should be an Integer") haveErrors = true elsif (noOfGroups < 1 or noOfGroups > 999) - additionalErrors[:noOfGroups].push("should be greater than 0 or less than 999") + additionalErrors[:noOfGroups].push("should range from 1 to 999") haveErrors = true end - # if parentGroupID != nil - # begin - # Group.find(parentGroupID) - # rescue Exception => e - # haveErrors = true - # additionalErrors[:parentGroupID].push("group not found with id "+parentGroupID) - # end - # 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 @@ -35,7 +31,7 @@ def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLe additionalErrors[:noOfLeafs].push("should be an Integer") haveErrors = true elsif (noOfLeafs != nil and (noOfLeafs < 1 or noOfLeafs > 999)) - additionalErrors[:noOfLeafs].push("should be greater than 0 or less than 999") + additionalErrors[:noOfLeafs].push("should range from 1 to 999") haveErrors = true end if (conjoin != nil) @@ -43,7 +39,7 @@ def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLe additionalErrors[:conjoin].push("should be a Boolean") haveErrors = true elsif (conjoin and (noOfLeafs != nil and noOfLeafs == 1)) - additionalErrors[:conjoin].push("should be false if noOfLeafs is 1") + additionalErrors[:conjoin].push("should be false if the number of leaves is 1") haveErrors = true end end @@ -52,10 +48,10 @@ def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLe additionalErrors[:oddMemberLeftOut].push("should be an Integer") haveErrors = true elsif (oddMemberLeftOut < 1 or oddMemberLeftOut > noOfLeafs) - additionalErrors[:oddMemberLeftOut].push("should be greater than 0 and less than noOfLeafs") + additionalErrors[:oddMemberLeftOut].push("should range from 1 to the number of leaves") haveErrors = true elsif (noOfLeafs.even?) - additionalErrors[:oddMemberLeftOut].push("should only be 0 if noOfLeafs is even") + additionalErrors[:oddMemberLeftOut].push("should be empty if the number of leaves is even") haveErrors = true end end @@ -84,9 +80,7 @@ def validateAdditionalGroupParams(noOfGroups, parentGroupID, memberOrder, noOfLe def validateGroupBatchDelete(allGroups) errors = [] allGroups.each do |groupID| - begin - Group.find(groupID) - rescue Exception => e + unless Group.where(id: groupID).exists? errors.push("group not found with id "+groupID) end end @@ -100,9 +94,7 @@ def validateGroupBatchUpdate(allGroups) error = {id: [], attributes: {type: []}} groupID = group_params[:id] type = group_params[:attributes][:type] - begin - Group.find(groupID) - rescue Exception => e + unless Group.where(id: groupID).exists? haveError = true error[:id].push("group not found with id "+groupID) end @@ -118,4 +110,4 @@ def validateGroupBatchUpdate(allGroups) end end -end \ No newline at end of file +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 index 72231bb1..00cc21b9 100644 --- a/viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb +++ b/viscoll-api/app/helpers/validation_helper/leaf_validation_helper.rb @@ -45,26 +45,26 @@ def validateAdditionalLeafParams(project_id, parentGroupID, memberOrder, noOfLea elsif (!noOfLeafs.is_a?(Integer)) additionalErrors[:noOfLeafs].push("should be an Integer") elsif (noOfLeafs < 1 or noOfLeafs > 999) - additionalErrors[:noOfLeafs].push("should be greater than 0 or less than 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 noOfLeafs is 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 be greater than 0 and less than noOfLeafs") + additionalErrors[:oddMemberLeftOut].push("should range from 1 to the number of leaves") elsif (noOfLeafs.even?) - additionalErrors[:oddMemberLeftOut].push("should only be 0 if noOfLeafs is even") + additionalErrors[:oddMemberLeftOut].push("should be present only if the number of leaves is odd") end end return additionalErrors end end -end \ No newline at end of file +end diff --git a/viscoll-api/app/mailers/feedback_mailer.rb b/viscoll-api/app/mailers/feedback_mailer.rb index 7b7dc32b..39b7d4b9 100644 --- a/viscoll-api/app/mailers/feedback_mailer.rb +++ b/viscoll-api/app/mailers/feedback_mailer.rb @@ -1,7 +1,9 @@ class FeedbackMailer < ApplicationMailer - def sendFeedback(title, message, current_user) + def sendFeedback(title, message, browserInformation, projectJSONExport, current_user) @title = title - @message = message + @message = message + @browserInformation = browserInformation + @projectJSONExport = projectJSONExport @user = User.find(current_user) mail( subject: title, diff --git a/viscoll-api/app/mailers/mailer.rb b/viscoll-api/app/mailers/mailer.rb index b47d16fd..37ae24af 100644 --- a/viscoll-api/app/mailers/mailer.rb +++ b/viscoll-api/app/mailers/mailer.rb @@ -1,28 +1,32 @@ 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 || "utlviscoll-admin@library.utoronto.ca" + 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) diff --git a/viscoll-api/app/models/group.rb b/viscoll-api/app/models/group.rb index 8b195ad5..2f2580b7 100644 --- a/viscoll-api/app/models/group.rb +++ b/viscoll-api/app/models/group.rb @@ -5,7 +5,8 @@ class Group # Fields field :title, type: String, default: "None" field :type, type: String, default: "None" - field :tacketed, type: String, default: "" + 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, ... ] diff --git a/viscoll-api/app/models/note.rb b/viscoll-api/app/models/note.rb index 49cc658a..84de2eb1 100644 --- a/viscoll-api/app/models/note.rb +++ b/viscoll-api/app/models/note.rb @@ -23,26 +23,26 @@ class Note def update_objects_before_delete self.objects[:Group].each do |groupID| if group = Group.where(:id => groupID).first - @group.notes.delete(self) - @group.save + 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 + 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 + 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 + side.notes.delete(self) + side.save end end end diff --git a/viscoll-api/app/models/project.rb b/viscoll-api/app/models/project.rb index abd2daad..ea3fb0ab 100644 --- a/viscoll-api/app/models/project.rb +++ b/viscoll-api/app/models/project.rb @@ -6,24 +6,22 @@ class Project 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, name: "", url: "", images: [{label: "", url: ""}]} } + field :manifests, type: Hash, default: lambda { { } } # (eg) { "1234556": { id: "123456, name: "", 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 - has_many :leafs, dependent: :destroy - has_many :sides, dependent: :destroy - has_many :notes, dependent: :destroy + has_many :groups, dependent: :delete + has_many :leafs, dependent: :delete + has_many :sides, dependent: :delete + has_many :notes, dependent: :delete # Validations validates_presence_of :title, :message => "Project title is required." validates_uniqueness_of :title, :message => "Project title: '%{value}', must be unique.", scope: :user - before_destroy :destroy_groups - def add_groupIDs(groupIDs, index) if self.groupIDs.length == 0 self.groupIDs = groupIDs @@ -38,11 +36,4 @@ def remove_groupID(groupID) self.save() end - def destroy_groups - self.groups.each do |group| - if group.nestLevel == 1 - group.destroy - end - end - end end diff --git a/viscoll-api/app/models/side.rb b/viscoll-api/app/models/side.rb index 9c9b99e4..a333484e 100644 --- a/viscoll-api/app/models/side.rb +++ b/viscoll-api/app/models/side.rb @@ -20,7 +20,8 @@ class Side # If linked to note(s), remove link from the note(s)'s side def unlink_notes self.notes.each do | note | - note.objects[:Side].delete(self.id.to_s) + note.objects[:Recto].delete(self.id.to_s) + note.objects[:Verso].delete(self.id.to_s) note.save end end diff --git a/viscoll-api/app/views/exports/show.json.jbuilder b/viscoll-api/app/views/exports/show.json.jbuilder index d28ee121..700404bd 100644 --- a/viscoll-api/app/views/exports/show.json.jbuilder +++ b/viscoll-api/app/views/exports/show.json.jbuilder @@ -1,9 +1,4 @@ json.project @data[:project] -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] diff --git a/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb b/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb index f1f26e08..6cd9fc3e 100644 --- a/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb +++ b/viscoll-api/app/views/feedback_mailer/sendFeedback.html.erb @@ -1,3 +1,21 @@ -

<%= @message %>

- -

Submitted by: <%= @user.name %> (<%= @user.email %>)

\ No newline at end of file +
+
+ +
+
+ +
+

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/config/initializers/rails_jwt_auth.rb b/viscoll-api/config/initializers/rails_jwt_auth.rb index 7bc1978f..15b7f935 100644 --- a/viscoll-api/config/initializers/rails_jwt_auth.rb +++ b/viscoll-api/config/initializers/rails_jwt_auth.rb @@ -29,6 +29,7 @@ # 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 diff --git a/viscoll-api/public/viscoll-datamodel2.rng b/viscoll-api/public/viscoll-datamodel2.rng new file mode 100644 index 00000000..ab405180 --- /dev/null +++ b/viscoll-api/public/viscoll-datamodel2.rng @@ -0,0 +1,296 @@ + + + + + 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/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/helpers/controller_helper/export_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb index 2a381d6d..587e857b 100644 --- a/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/export_helper_spec.rb @@ -1,15 +1,69 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::ExportHelper. For example: -# -# describe ControllerHelper::ExportHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ControllerHelper::ExportHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 '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: ['Hand', 'Ink', 'Unknown'] + }) + expect(result[:groups]).to eq({ + 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"]} + }) + expect(result[:leafs]).to eq({ + 1 => {:params=>{:material=>"Paper", :type=>"Original", :attachment_method=>"None", :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", :attachment_method=>"None", :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", :attachment_method=>"None", :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", :attachment_method=>"None", :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", :attachment_method=>"None", :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", :attachment_method=>"None", :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=>"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} + }) + expect(result[:versos]).to eq({ + 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} + }) + 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 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 index 85b8d8ad..d0c65f31 100644 --- a/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb @@ -1,15 +1,182 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::FilterHelper. For example: -# -# describe ControllerHelper::FilterHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ControllerHelper::FilterHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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_leaf_order, attached_above, attached_below, stub]') + end + + it 'should accept valid parameters for conditions' do + ['type', 'material', 'conjoined_to', 'attached_to', '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, 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 index c5754a6e..ff7051d5 100644 --- a/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/groups_helper_spec.rb @@ -1,15 +1,25 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::GroupsHelper. For example: -# -# describe ControllerHelper::GroupsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ControllerHelper::GroupsHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 index 5c1299fa..1bb22187 100644 --- a/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/import_helper_spec.rb @@ -1,15 +1,100 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::ImportHelper. For example: -# -# describe ControllerHelper::ImportHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end -RSpec.describe ControllerHelper::ImportHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" +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", "attachment_method" => "None", "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", "attachment_method" => "None", "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", "attachment_method" => "None", "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", "attachment_method" => "None", "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", "attachment_method" => "None", "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", "attachment_method" => "None", "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/leafs_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb index 508d6b54..fff31e21 100644 --- a/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb @@ -1,15 +1,155 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::LeafsHelper. For example: -# -# describe ControllerHelper::LeafsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ControllerHelper::LeafsHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 + 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 index e3d0c7f5..fb9cf038 100644 --- a/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb @@ -1,15 +1,119 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ControllerHelper::ProjectsHelper. For example: -# -# describe ControllerHelper::ProjectsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ControllerHelper::ProjectsHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 } + ]) + 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 + 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. Provided by Villanova ...', + '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 + + describe 'Roman numerals' do + it 'should convert properly' do + { + 1999 => "mcmxcix", + 1000 => "m", + 900 => "cm", + 678 => "dclxxviii", + 666 => "dclxvi", + 444 => "cdxliv", + 500 => "d", + 400 => "cd", + 100 => "c", + 90 => "xc", + 50 => "l", + 40 => "xl", + 10 => "x", + 9 => "ix", + 5 => "v", + 4 => "iv", + 1 => "i" + }.each do |value, target| + expect(to_roman(value)).to eq target + end + 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 index e777e749..2736eca4 100644 --- a/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb +++ b/viscoll-api/spec/helpers/validation_helper/group_validation_helper_spec.rb @@ -1,15 +1,186 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ValidationHelper::GroupValidationHelper. For example: -# -# describe ValidationHelper::GroupValidationHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ValidationHelper::GroupValidationHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 index dce669c6..8dbced9d 100644 --- a/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb +++ b/viscoll-api/spec/helpers/validation_helper/leaf_validation_helper_spec.rb @@ -1,15 +1,145 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ValidationHelper::LeafValidationHelper. For example: -# -# describe ValidationHelper::LeafValidationHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ValidationHelper::LeafValidationHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 index 067c1309..7a326e7b 100644 --- a/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb +++ b/viscoll-api/spec/helpers/validation_helper/project_validation_helper_spec.rb @@ -1,15 +1,72 @@ require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ValidationHelper::ProjectValidationHelper. For example: -# -# describe ValidationHelper::ProjectValidationHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ValidationHelper::ProjectValidationHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + 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 index 8af03277..b386360a 100644 --- a/viscoll-api/spec/mailers/feedback_spec.rb +++ b/viscoll-api/spec/mailers/feedback_spec.rb @@ -6,7 +6,7 @@ @user = User.create(:name => "user", :email => "user@mail.com", :password => "user") end - let(:mail) { FeedbackMailer.sendFeedback("Title of feedback", "My message", @user.id)} + 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") diff --git a/viscoll-api/spec/models/group_spec.rb b/viscoll-api/spec/models/group_spec.rb index 7cb6ff6e..d7e6ccff 100644 --- a/viscoll-api/spec/models/group_spec.rb +++ b/viscoll-api/spec/models/group_spec.rb @@ -1,81 +1,105 @@ -# require 'rails_helper' +require 'rails_helper' -# RSpec.describe Group, type: :model do -# it { is_expected.to be_mongoid_document } -# it { is_expected.to have_field(:order).of_type(Integer) } -# it { is_expected.to have_field(:title).of_type(String) } -# it { is_expected.to have_field(:type).of_type(String) } -# it { is_expected.to belong_to(:project).with_foreign_key(:project_id) } -# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } -# it { is_expected.to have_and_belong_to_many(:groupings).with_foreign_key(:grouping_ids) } - -# before(:each) do -# @project = FactoryGirl.create(:project) -# @group = @project.get_root_groups[0] -# @leaf = FactoryGirl.create(:leaf, project: @project) -# @group.add_members([@leaf], 1) -# @nestedGroup = FactoryGirl.create(:group, project: @project, groupOrder: 2) -# @group.add_members([@nestedGroup], 2) -# end - -# it "can get its members" do -# expect(@group.get_member_objects.size).to eq(2) -# expect(@nestedGroup.get_member_objects.size).to eq(0) -# expect(@group.get_members.size).to eq(2) -# expect(@nestedGroup.get_members.size).to eq(0) -# end - -# it "can get a flattened list of members" do -# # Add a leaf in the nested group -# nestedLeaf = FactoryGirl.create(:leaf, project: @project) -# @nestedGroup.add_members([nestedLeaf], 1) -# # Expect 4 items: itself, leaf, nested group and nested leaf -# expect(@group.get_flattened_nested_members.size).to eq(4) -# end - -# it "can get all members, including special leaves 'none' and 'binding'" do -# # Expect leaf and nested group -# expect(@group.get_members_special.size).to eq(2) -# end - -# it "can delete a member" do -# expect(@group.get_members.size).to eq(2) -# @group.delete_member(@nestedGroup) -# @group.reload -# expect(@group.get_members.size).to eq(1) -# end - -# it "can delete all its members" do -# @group.destroy_members -# expect(@group.get_members.size).to eq(0) -# end - -# it "can get its parent" do -# expect(@nestedGroup.get_parent).not_to be_nil -# expect(@nestedGroup.get_parent_id).not_to be_nil -# expect(@group.get_parent).to be_nil -# expect(@group.get_parent_id).to be_nil -# end - -# it "does not re-add an existing member" do -# expect(@group.add_members([@leaf], 1)).to eq(false) -# end - -# it "updates existing member orders when a new member gets inserted" do -# newLeaf = FactoryGirl.create(:leaf, project: @project) -# expect(@group.add_members([newLeaf], 1)).to eq(true) -# # Verify order of members -# groupings = @group.get_member_groupings -# expect(groupings[0].member).to eq(newLeaf) -# expect(groupings[1].member).to eq(@leaf) -# expect(groupings[2].member).to eq(@nestedGroup) -# end +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) } -# it "if destroyed, it removes parent's association to this group if any" do -# expect(@group.get_members.size).to eq(2) -# @nestedGroup.destroy -# @group.reload -# expect(@group.get_members.size).to eq(1) -# end + 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 -# 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.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/leaf_spec.rb b/viscoll-api/spec/models/leaf_spec.rb index 170165d2..49163ab7 100644 --- a/viscoll-api/spec/models/leaf_spec.rb +++ b/viscoll-api/spec/models/leaf_spec.rb @@ -1,43 +1,65 @@ -# require 'rails_helper' +require 'rails_helper' -# RSpec.describe Leaf, type: :model do -# it { is_expected.to be_mongoid_document } -# it { is_expected.to have_field(:order).of_type(Integer) } -# 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(:attachment_method).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 belong_to(:project).with_foreign_key(:project_id) } -# it { is_expected.to have_many(:sides).with_foreign_key(:leaf_id) } -# it { is_expected.to have_many(:groupings).with_foreign_key(:member_id) } -# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } +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(:attachment_method).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, order: 1) -# @leaf2 = FactoryGirl.create(:leaf, project: @project, order: 2) -# @group = FactoryGirl.create(:quire) -# end + before(:each) do + @project = FactoryGirl.create(:project) + @leaf = FactoryGirl.create(:leaf, project: @project) + end -# it "can tell you what type it is" do -# expect(@leaf.type_of).to eq "Leaf" -# end - -# it "can get its parent" do -# expect(@leaf.get_parent).to eq false -# @group.add_member(@leaf, 1) -# expect(@leaf.get_parent).to eq @group -# end - -# it "deletes its grouping relationships when it gets deleted" do -# @group.add_member(@leaf, 1) -# memberCount = @group.get_members.size -# @leaf.destroy -# @group.reload -# expect(@group.get_members.size).to_not eq(memberCount) -# end - -# end \ No newline at end of file + 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 index 00b6a437..2e502605 100644 --- a/viscoll-api/spec/models/note_spec.rb +++ b/viscoll-api/spec/models/note_spec.rb @@ -1,53 +1,77 @@ -# require 'rails_helper' +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 belong_to(:project).with_foreign_key(:project_id) } +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) } -# it "updates linked objects before it gets deleted" do -# project = FactoryGirl.create(:project) -# note = FactoryGirl.create(:note, project: project) -# group = project.get_root_groups[0] -# group2 = FactoryGirl.create(:group, project: project, groupOrder: 2) -# leaf = project.leafs[0] -# leaf2 = FactoryGirl.create(:leaf, project: project) -# side = leaf.sides[0] -# side2 = leaf.sides[1] + 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 -# # Link note to objects -# group.notes.push(note) -# group2.notes.push(note) -# leaf.notes.push(note) -# leaf2.notes.push(note) -# side.notes.push(note) -# side2.notes.push(note) -# note.objects[:Group].push(group.id.to_s) -# note.objects[:Group].push(group2.id.to_s) -# note.objects[:Leaf].push(leaf.id.to_s) -# note.objects[:Leaf].push(leaf2.id.to_s) -# note.objects[:Side].push(side.id.to_s) -# note.objects[:Side].push(side2.id.to_s) - -# expect(group.notes.size).to eq(1) -# expect(group2.notes.size).to eq(1) -# expect(leaf.notes.size).to eq(1) -# expect(leaf2.notes.size).to eq(1) -# expect(side.notes.size).to eq(1) -# expect(side2.notes.size).to eq(1) -# group2.destroy -# leaf2.destroy -# side2.destroy - -# note.destroy -# group.reload -# leaf.reload -# side.reload -# expect(group.notes.size).to eq(0) -# expect(leaf.notes.size).to eq(0) -# expect(side.notes.size).to eq(0) -# end -# 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..cf8e7fbd --- /dev/null +++ b/viscoll-api/spec/models/project_spec.rb @@ -0,0 +1,63 @@ +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 +end diff --git a/viscoll-api/spec/models/side_spec.rb b/viscoll-api/spec/models/side_spec.rb index f56d66d6..13df31c8 100644 --- a/viscoll-api/spec/models/side_spec.rb +++ b/viscoll-api/spec/models/side_spec.rb @@ -1,14 +1,34 @@ -# require 'rails_helper' +require 'rails_helper' -# RSpec.describe Side, type: :model do -# it { is_expected.to be_mongoid_document } -# it { is_expected.to have_field(:order).of_type(Integer) } -# 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(:uri).of_type(String) } -# it { is_expected.to have_field(:script_direction).of_type(String) } -# it { is_expected.to have_field(:image).of_type(String) } -# it { is_expected.to belong_to(:leaf).with_foreign_key(:leaf_id) } -# it { is_expected.to have_and_belong_to_many(:notes).with_foreign_key(:note_ids) } - -# end \ No newline at end of file +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 + @project = FactoryGirl.create(:project) + @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 + end +end diff --git a/viscoll-api/spec/requests/feedback/create_feedback_spec.rb b/viscoll-api/spec/requests/feedback/create_feedback_spec.rb index e40ba682..927435ac 100644 --- a/viscoll-api/spec/requests/feedback/create_feedback_spec.rb +++ b/viscoll-api/spec/requests/feedback/create_feedback_spec.rb @@ -24,6 +24,27 @@ 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 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..1596f6e8 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb @@ -0,0 +1,185 @@ +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', attachment_method: 'Glued', 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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + end + + it 'updates the affected leafs' do + pending 'BUG: Should conjoin first-fifth, second-fourth and leave the third alone' + 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 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..a0104621 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_create_spec.rb @@ -0,0 +1,161 @@ +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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..652a7803 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb @@ -0,0 +1,179 @@ +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', attachment_method: 'Glued', 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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each do |leaf| + if Leaf.where(id: leaf.id).exists? + leaf.reload + end + end + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..5a46c528 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb @@ -0,0 +1,167 @@ +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', attachment_method: 'Glued', 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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload unless leaf.id == @leafs[1].id } + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..52629c1c --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb @@ -0,0 +1,199 @@ +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', attachment_method: 'Glued', 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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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..312291c3 --- /dev/null +++ b/viscoll-api/spec/requests/leafs/leafs_update_spec.rb @@ -0,0 +1,151 @@ +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", + "attachment_method": "Glued", + "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'} + @body = JSON.parse(response.body) + @project.reload + @group.reload + @leafs.each { |leaf| leaf.reload } + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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 index bbce74cb..953b438d 100644 --- a/viscoll-api/spec/requests/notes/notes_create_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_create_spec.rb @@ -52,6 +52,22 @@ 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 @@ -63,6 +79,23 @@ 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 diff --git a/viscoll-api/spec/requests/notes/notes_create_type_spec.rb b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb index a7d8b7ee..e0a5f4b0 100644 --- a/viscoll-api/spec/requests/notes/notes_create_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb @@ -72,6 +72,24 @@ 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 diff --git a/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb index dd4ed283..1936b844 100644 --- a/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb @@ -88,6 +88,24 @@ 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 diff --git a/viscoll-api/spec/requests/notes/notes_update_type_spec.rb b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb index 979c3ae4..8268e5e9 100644 --- a/viscoll-api/spec/requests/notes/notes_update_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb @@ -115,6 +115,24 @@ 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 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/delete_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb new file mode 100644 index 00000000..40107e20 --- /dev/null +++ b/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb @@ -0,0 +1,161 @@ +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 + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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/update_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb new file mode 100644 index 00000000..8d4ff4fe --- /dev/null +++ b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb @@ -0,0 +1,180 @@ +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 + @body = JSON.parse(response.body) + end + + it 'returns 200' do + expect(response).to have_http_status(:ok) + 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/spec_helper.rb b/viscoll-api/spec/spec_helper.rb index 619b1482..4f662aaf 100644 --- a/viscoll-api/spec/spec_helper.rb +++ b/viscoll-api/spec/spec_helper.rb @@ -1,14 +1,19 @@ # Load and launch SimpleCov at the very top require 'simplecov' -SimpleCov.start do +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 diff --git a/viscoll-app/package-lock.json b/viscoll-app/package-lock.json index 03513cd3..9e8efa90 100644 --- a/viscoll-app/package-lock.json +++ b/viscoll-app/package-lock.json @@ -10,8 +10,7 @@ "dev": true }, "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", "dev": true }, @@ -21,13 +20,11 @@ "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==" }, "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "version": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "dependencies": { "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" } } @@ -47,14 +44,12 @@ } }, "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "dependencies": { "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "version": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } @@ -81,25 +76,21 @@ "dev": true }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true }, "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "version": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", "dev": true }, "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=" }, "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "version": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, @@ -115,31 +106,26 @@ "dev": true }, "ansi-align": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", + "version": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=", "dev": true }, "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", "dev": true }, "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "version": "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", + "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, @@ -161,14 +147,12 @@ "dev": true }, "aria-query": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz", + "version": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz", "integrity": "sha1-heMVLNjMW6sY2+1hzZxPzlT6ecM=", "dev": true }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=" }, "arr-flatten": { @@ -189,8 +173,7 @@ "dev": true }, "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "version": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, @@ -201,8 +184,7 @@ "dev": true }, "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "version": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "dev": true }, @@ -225,25 +207,21 @@ "dev": true }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true }, "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "version": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" }, "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, @@ -264,8 +242,7 @@ "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=" }, "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "version": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=" }, "assert-plus": { @@ -280,8 +257,7 @@ "integrity": "sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI=" }, "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "version": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, @@ -291,8 +267,7 @@ "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==" }, "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "version": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" }, "asynckit": { @@ -301,14 +276,8 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "attr-accept": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.0.tgz", - "integrity": "sha1-tc01In8WOTWo8d4Q7T66FpQfa+Y=" - }, "autoprefixer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.0.tgz", + "version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.0.tgz", "integrity": "sha1-rkkTrcIh+mylrTpvgDn2pcBrOHc=", "dev": true }, @@ -330,14 +299,12 @@ "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=" }, "axobject-query": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "version": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", "dev": true }, "babel-code-frame": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", "dev": true }, @@ -348,14 +315,12 @@ "dev": true }, "babel-eslint": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "version": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", "dev": true }, "babel-generator": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", "dev": true }, @@ -450,8 +415,7 @@ "dev": true }, "babel-jest": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", + "version": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=", "dev": true }, @@ -482,8 +446,7 @@ } }, "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true }, @@ -500,8 +463,7 @@ "dev": true }, "babel-plugin-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", + "version": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", "integrity": "sha1-GN3oS/POMp/d8/QQP66SFFbY5Yc=", "dev": true, "dependencies": { @@ -514,8 +476,7 @@ } }, "babel-plugin-jest-hoist": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz", + "version": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz", "integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c=", "dev": true }, @@ -568,8 +529,7 @@ "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", + "version": "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 }, @@ -786,8 +746,7 @@ "dev": true }, "babel-plugin-transform-object-rest-spread": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", + "version": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", "integrity": "sha1-h11ryb52HFiirj/u5dxIldjH+SE=", "dev": true }, @@ -882,8 +841,7 @@ "dev": true }, "babel-preset-jest": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", + "version": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=", "dev": true }, @@ -930,26 +888,22 @@ } }, "babel-template": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", "dev": true }, "babel-traverse": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", "dev": true }, "babel-types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", "dev": true }, "babylon": { - "version": "6.17.4", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "version": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", "integrity": "sha1-Pot0AriNIsNCPhN6FXeIOxX/hpo=", "dev": true }, @@ -960,8 +914,7 @@ "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base62": { @@ -970,13 +923,11 @@ "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=" }, "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "version": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" }, "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "version": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, @@ -988,8 +939,7 @@ "optional": true }, "big.js": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "version": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=" }, "binary-extensions": { @@ -998,8 +948,7 @@ "integrity": "sha1-ZlBsFs5vTWkopbPNajPKQelB43s=" }, "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", "dev": true }, @@ -1009,8 +958,7 @@ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "version": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, @@ -1026,27 +974,23 @@ "integrity": "sha1-uUzGklumteB8QhpY5gHORhEmRXI=" }, "boxen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", + "version": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=", "dev": true, "dependencies": { "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true } } }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=" }, "brorand": { @@ -1094,8 +1038,7 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=" }, "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "version": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=" }, "browserslist": { @@ -1131,8 +1074,7 @@ } }, "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "version": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dependencies": { "isarray": { @@ -1148,13 +1090,11 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "version": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, "bytes": { @@ -1164,14 +1104,12 @@ "dev": true }, "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "version": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, @@ -1182,33 +1120,28 @@ "dev": true }, "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" }, "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "version": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "dependencies": { "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true } } }, "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "version": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "dev": true, "dependencies": { "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true } @@ -1227,14 +1160,12 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "version": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, "case-sensitive-paths-webpack-plugin": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-1.1.4.tgz", + "version": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-1.1.4.tgz", "integrity": "sha1-iq7dVpmobKwrNM9A2bQUV1iXhHI=", "dev": true }, @@ -1251,8 +1182,7 @@ "dev": true }, "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=" }, "chain-function": { @@ -1261,14 +1191,12 @@ "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w=" }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "dependencies": { "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } @@ -1324,8 +1252,7 @@ } }, "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "version": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=" }, "ci-info": { @@ -1346,8 +1273,7 @@ "dev": true }, "clap": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", + "version": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", "integrity": "sha1-WckP4+E3EEdG/xlGmiemNP9oyFc=", "dev": true }, @@ -1378,20 +1304,17 @@ } }, "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "version": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "version": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true }, "cli-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "version": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", "dev": true }, @@ -1407,19 +1330,16 @@ "dev": true }, "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=" }, "clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "version": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", "dev": true }, "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { @@ -1429,8 +1349,7 @@ "dev": true }, "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codemirror": { @@ -1446,14 +1365,12 @@ "dev": true }, "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "version": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", "dev": true }, "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "version": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", "dev": true }, @@ -1464,20 +1381,17 @@ "dev": true }, "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "version": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true }, "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "version": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", "dev": true }, "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, @@ -1506,8 +1420,7 @@ "dev": true }, "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "version": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, @@ -1524,49 +1437,41 @@ "dev": true }, "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true }, "configstore": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "version": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", "dev": true, "dependencies": { "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "version": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", "dev": true } } }, "connect-history-api-fallback": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", + "version": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", "integrity": "sha1-5R0X+PDvDbkKZP20feMFFVbp8Wk=", "dev": true }, "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "version": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=" }, "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "version": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "version": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, @@ -1589,8 +1494,7 @@ "dev": true }, "convert-source-map": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", "dev": true }, @@ -1667,8 +1571,7 @@ "integrity": "sha1-VpwFCRi+ZIazg3VSAorgRmtxcIY=" }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { @@ -1691,8 +1594,7 @@ "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=" }, "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "version": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true }, @@ -1707,8 +1609,7 @@ "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=" }, "create-react-class": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "version": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=" }, "cross-spawn": { @@ -1729,8 +1630,7 @@ "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==" }, "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "version": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, @@ -1740,80 +1640,68 @@ "integrity": "sha1-msfgL3Y8+F2UAXZmVl7WiltfMhU=" }, "css-loader": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", + "version": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", "integrity": "sha1-IgMlWZ+PAEUtnOtMPKbIpmeYZC0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "version": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true }, "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "version": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", "dev": true, "dependencies": { "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "version": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true } } }, "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "version": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", "dev": true }, "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "version": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", "dev": true }, "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "version": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, "dependencies": { "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", "dev": true }, "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true }, "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "version": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", "dev": true }, @@ -1830,19 +1718,16 @@ "dev": true }, "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "version": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true }, "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "version": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=" }, "damerau-levenshtein": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "version": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", "dev": true }, @@ -1861,29 +1746,24 @@ } }, "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "version": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=" }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", "dev": true }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, @@ -1894,20 +1774,17 @@ "dev": true }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "version": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true }, "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "version": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true }, @@ -1935,14 +1812,12 @@ "dev": true }, "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true }, "detect-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "version": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", "dev": true }, @@ -1963,19 +1838,8 @@ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=" }, - "disposables": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.1.tgz", - "integrity": "sha1-BkcnoltU9QK9griaot+4358bOeM=" - }, - "dnd-core": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.5.4.tgz", - "integrity": "sha512-BcI782MfTm3wCxeIS5c7tAutyTwEIANtuu3W6/xkoJRwiqhRXKX3BbGlycUxxyzMsKdvvoavxgrC3EMPFNYL9A==" - }, "doctrine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true, "dependencies": { @@ -1988,14 +1852,12 @@ } }, "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "version": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "dev": true, "dependencies": { "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "version": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", "dev": true } @@ -2007,22 +1869,19 @@ "integrity": "sha1-MgPgf+0he9H0JLAZc1WC/Deyglo=" }, "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "version": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "dependencies": { "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true } } }, "dom-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", + "version": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=", "dev": true }, @@ -2032,49 +1891,41 @@ "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" }, "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "version": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" }, "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", "dev": true }, "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "version": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", "dev": true }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "version": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true }, "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "version": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", "dev": true }, "dotenv": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "version": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", "dev": true }, "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "version": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "version": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true }, @@ -2109,8 +1960,7 @@ "dev": true }, "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "version": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" }, "encodeurl": { @@ -2130,8 +1980,7 @@ "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=" }, "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "version": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, @@ -2142,13 +1991,11 @@ "dev": true }, "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=" }, "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=" }, "es-abstract": { @@ -2181,13 +2028,11 @@ "integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=" }, "es6-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "version": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=" }, "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "version": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=" }, "es6-object-assign": { @@ -2202,29 +2047,24 @@ "integrity": "sha1-3aA8qPn4m8WX5omEKSnee6jOvfA=" }, "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "version": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=" }, "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "version": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=" }, "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "version": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=" }, "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "version": "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", + "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, @@ -2256,19 +2096,16 @@ } }, "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "version": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=" }, "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "version": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "dev": true, "dependencies": { "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true } @@ -2281,14 +2118,12 @@ "dev": true }, "eslint-import-resolver-node": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", + "version": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", "dev": true }, "eslint-loader": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", + "version": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", "integrity": "sha1-ULFY3WJy3O+5fphCVIN/gaWALOA=", "dev": true }, @@ -2299,20 +2134,17 @@ "dev": true }, "eslint-plugin-flowtype": { - "version": "2.33.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.33.0.tgz", + "version": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.33.0.tgz", "integrity": "sha1-sng4FO0t3PcplTuPZf9zyQyr7ks=", "dev": true }, "eslint-plugin-import": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", + "version": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", "dev": true, "dependencies": { "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "version": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true }, @@ -2325,14 +2157,12 @@ } }, "eslint-plugin-jsx-a11y": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz", + "version": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz", "integrity": "sha1-SpOfduwSUBBSiCMzG/lIzFczgLY=", "dev": true }, "eslint-plugin-react": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.0.1.tgz", + "version": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.0.1.tgz", "integrity": "sha1-54EH4eVZxuKxd4a7Z8LioBCtDS8=", "dev": true }, @@ -2354,24 +2184,20 @@ "dev": true }, "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "version": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "dev": true }, "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "version": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=" }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, @@ -2382,24 +2208,20 @@ "dev": true }, "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "version": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=" }, "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "version": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", "dev": true }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "version": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "version": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "dev": true }, @@ -2415,19 +2237,16 @@ "dev": true }, "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", "dev": true }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=" }, "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=" }, "express": { @@ -2463,13 +2282,11 @@ "dev": true }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=" }, "extract-text-webpack-plugin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", + "version": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", "integrity": "sha1-aTFbiF+Hbb+W04Gfap8cynrr8Vk=", "dev": true }, @@ -2492,25 +2309,21 @@ } }, "fast-deep-equal": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz", + "version": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz", "integrity": "sha1-XG9FmaumszPuM0Li7ZeGcvEAH40=" }, "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "version": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", "dev": true }, "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "version": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "dev": true }, @@ -2533,26 +2346,22 @@ } }, "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "version": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true }, "file-loader": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", + "version": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", "integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", "dev": true }, "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" }, "fileset": { @@ -2562,19 +2371,16 @@ "dev": true }, "filesize": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz", + "version": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz", "integrity": "sha1-UxSeo0YOOy4CSWKlFkiqVyz5gSI=", "dev": true }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=" }, "filled-array": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", + "version": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q=", "dev": true }, @@ -2585,8 +2391,7 @@ "dev": true }, "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "version": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true }, @@ -2616,14 +2421,12 @@ } }, "flat-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", "dev": true }, "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "version": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", "dev": true }, @@ -2633,18 +2436,15 @@ "integrity": "sha512-Suw6KewLV2hReSyEOeql+UUkBVyiBm3ok1VPrVFRZnQInWpdoZbbiG5i8aJVSjTr0yQ4Ava0Sh6/joCg1Brdqw==" }, "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=" }, "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "version": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "forever-agent": { @@ -2672,790 +2472,1256 @@ "dev": true }, "fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", "dev": true }, "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", - "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", - "dev": true - }, - "function.name-polyfill": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/function.name-polyfill/-/function.name-polyfill-1.0.5.tgz", - "integrity": "sha1-00m7TiSjJPCBIEVe54oEFCsSV7s=", - "dev": true - }, - "function.prototype.name": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.0.3.tgz", - "integrity": "sha512-5EblxZUdioXi2JiMZ9FUbwYj40eQ9MFHyzFLBSPdlRl3SO8l7SLWuAnQ/at/1Wi4hjJwME/C5WpF2ZfAc8nGNw==", - "dev": true - }, - "fuse.js": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.0.5.tgz", - "integrity": "sha1-tY2Fh4gCMh3pRGFlSUe5OvEIZyc=" - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "optional": true, "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "bundled": true + }, + "boom": { + "version": "2.10.1", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true + }, + "buffer-shims": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "github-slugger": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.1.3.tgz", - "integrity": "sha1-MUpudZoYwrDMV2DVEsy6tUnFSac=", - "dev": true, - "dependencies": { - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "dev": true - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=" - }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" - } - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true - }, - "glogg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", - "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", - "dev": true - }, - "got": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", - "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", - "dev": true - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", - "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", - "dev": true - }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", - "dev": true - }, - "handlebars": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", - "dev": true, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "bundled": true }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true + "caseless": { + "version": "0.12.0", + "bundled": true, + "optional": true }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, + "co": { + "version": "4.6.0", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, "optional": true, "dependencies": { - "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true, + "assert-plus": { + "version": "1.0.0", + "bundled": true, "optional": true } } }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, + "debug": { + "version": "2.6.8", + "bundled": true, "optional": true - } - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true - }, - "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 - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=" - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==" - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "highlight.js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", - "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", - "dev": true - }, - "history": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/history/-/history-4.6.3.tgz", - "integrity": "sha1-bXI6hxLFgda+836MJvSu3G64aWc=" - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "hoist-non-react-statics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", - "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" - }, - "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 - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz", - "integrity": "sha1-eb96eF6klf5mFl5zQVPzY/9UN9o=", - "dev": true - }, - "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-minifier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz", - "integrity": "sha512-iKRzQQDuTCsq0Ultbi/mfJJnR0D3AdZKTq966Gsp92xkmAPCV4Xi08qhJ0Dl3ZAWemSgJ7qZK+UsZc0gFqK6wg==", - "dev": true - }, - "html-webpack-plugin": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", - "integrity": "sha1-LnhjtX5f1I/iYzA+L/yTTDBk0Ak=", - "dev": true, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "optional": true }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "optional": true + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "optional": true + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true + }, + "request": { + "version": "2.81.0", + "bundled": true, + "optional": true + }, + "rimraf": { + "version": "2.6.1", + "bundled": true + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "optional": true + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true } } }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "function-bind": { + "version": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", "dev": true }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "function.name-polyfill": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/function.name-polyfill/-/function.name-polyfill-1.0.5.tgz", + "integrity": "sha1-00m7TiSjJPCBIEVe54oEFCsSV7s=", "dev": true }, - "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "function.prototype.name": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.0.3.tgz", + "integrity": "sha512-5EblxZUdioXi2JiMZ9FUbwYj40eQ9MFHyzFLBSPdlRl3SO8l7SLWuAnQ/at/1Wi4hjJwME/C5WpF2ZfAc8nGNw==", "dev": true }, - "http-proxy-middleware": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", - "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", - "dev": true, - "dependencies": { - "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-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true - } - } + "fuse.js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.0.5.tgz", + "integrity": "sha1-tY2Fh4gCMh3pRGFlSUe5OvEIZyc=" }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "generate-function": { + "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", "dev": true }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + "generate-object-property": { + "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true }, - "hyphenate-style-name": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", - "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" - }, - "iconv-lite": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", - "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + "get-caller-file": { + "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "get-stdin": { + "version": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + "github-slugger": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.1.3.tgz", + "integrity": "sha1-MUpudZoYwrDMV2DVEsy6tUnFSac=", + "dev": true, + "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + } + } }, - "ignore": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", - "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + "glob-base": { + "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" }, - "immutability-helper": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.4.0.tgz", - "integrity": "sha512-rW/L/56ZMo9NStMK85kFrUFFGy4NeJbCdhfrDHIZrFfxYtuwuxD+dT3mWMcdmrNO61hllc60AeGglCRhfZ1dZw==" + "glob-parent": { + "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=" }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "globals": { + "version": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", "dev": true }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "globby": { + "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", "dev": true }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "got": { + "version": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true }, - "inline-process-browser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", - "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" - }, - "inline-style-prefixer": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz", - "integrity": "sha1-DMyS5ZAv5uDSjZdcQlhEP4gGFfg=" + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, - "interpret": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", - "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=" - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=" - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "ipaddr.js": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", - "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", + "gzip-size": { + "version": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", "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=", + "handle-thing": { + "version": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, - "is-alphabetical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", - "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", - "dev": true + "handlebars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", + "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "optional": true + } + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true + } + } }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", "dev": true }, - "is-alphanumerical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", - "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "has": { + "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "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=" + "has-ansi": { + "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + "has-flag": { + "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=" }, - "is-callable": { + "hash.js": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==" }, - "is-ci": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", - "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true }, - "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=", + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "is-decimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", - "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", - "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=", + "highlight.js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + "history": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/history/-/history-4.6.3.tgz", + "integrity": "sha1-bXI6hxLFgda+836MJvSu3G64aWc=" }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=" + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true }, - "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=" + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=" + "hpack.js": { + "version": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true }, - "is-hexadecimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", - "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", + "html-comment-regex": { + "version": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", "dev": true }, - "is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "html-encoding-sniffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz", + "integrity": "sha1-eb96eF6klf5mFl5zQVPzY/9UN9o=", "dev": true }, - "is-my-json-valid": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", - "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "html-entities": { + "version": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", "dev": true }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "html-minifier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz", + "integrity": "sha512-iKRzQQDuTCsq0Ultbi/mfJJnR0D3AdZKTq966Gsp92xkmAPCV4Xi08qhJ0Dl3ZAWemSgJ7qZK+UsZc0gFqK6wg==", "dev": true }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=" + "html-webpack-plugin": { + "version": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", + "integrity": "sha1-LnhjtX5f1I/iYzA+L/yTTDBk0Ak=", + "dev": true, + "dependencies": { + "loader-utils": { + "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true + } + } }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true + "htmlparser2": { + "version": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "dependencies": { + "domutils": { + "version": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "http-deceiver": { + "version": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "dev": true }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "dev": true }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "http-proxy": { + "version": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", "dev": true }, - "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=", + "http-proxy-middleware": { + "version": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", + "dev": true, + "dependencies": { + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + "https-browserify": { + "version": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + "hyphenate-style-name": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", + "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" }, - "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 + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "icss-replace-symbols": { + "version": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "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=", + "ieee754": { + "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "ignore": { + "version": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", + "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", "dev": true }, - "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "immutability-helper": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.4.0.tgz", + "integrity": "sha512-rW/L/56ZMo9NStMK85kFrUFFGy4NeJbCdhfrDHIZrFfxYtuwuxD+dT3mWMcdmrNO61hllc60AeGglCRhfZ1dZw==" + }, + "imurmurhash": { + "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "indent-string": { + "version": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true }, - "is-root": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", - "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "indexes-of": { + "version": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "dev": true }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "indexof": { + "version": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", "dev": true }, - "is-typedarray": { + "inline-process-browser": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", + "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "inline-style-prefixer": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz", + "integrity": "sha1-DMyS5ZAv5uDSjZdcQlhEP4gGFfg=" + }, + "inquirer": { + "version": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true + }, + "interpret": { + "version": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=" + }, + "invariant": { + "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=" + }, + "invert-kv": { + "version": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", + "dev": true + }, + "is-absolute-url": { + "version": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-alphabetical": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", + "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", + "dev": true + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", + "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", + "dev": true + }, + "is-arrayish": { + "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=" + }, + "is-buffer": { + "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "is-builtin-module": { + "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + }, + "is-callable": { + "version": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-ci": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", + "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", + "dev": true + }, + "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=", + "dev": true + }, + "is-decimal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", + "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", + "dev": true + }, + "is-directory": { + "version": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=" + }, + "is-extendable": { + "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-finite": { + "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=" + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=" + }, + "is-hexadecimal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", + "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", + "dev": true + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, + "is-my-json-valid": { + "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "dev": true + }, + "is-npm": { + "version": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=" + }, + "is-obj": { + "version": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-path-cwd": { + "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true + }, + "is-path-inside": { + "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "dev": true + }, + "is-plain-obj": { + "version": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-posix-bracket": { + "version": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "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-property": { + "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-redirect": { + "version": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "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=", + "dev": true + }, + "is-resolvable": { + "version": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true + }, + "is-retry-allowed": { + "version": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-root": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", + "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "dev": true + }, + "is-stream": { + "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "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": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "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-utf8": { + "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, "is-whitespace-character": { @@ -3488,8 +3754,7 @@ "dev": true }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dependencies": { "isarray": { @@ -3525,8 +3790,7 @@ } }, "istanbul-lib-coverage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "version": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", "integrity": "sha1-c7+5mIhSmUFck9OKPprfeEp3qdo=", "dev": true }, @@ -3709,8 +3973,7 @@ "dev": true }, "js-base64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "version": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=", "dev": true }, @@ -3758,8 +4021,7 @@ } }, "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "version": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, @@ -3780,24 +4042,20 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" }, "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "jsonfile": { @@ -3807,13 +4065,11 @@ "dev": true }, "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "version": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, @@ -3891,8 +4147,7 @@ } }, "jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "version": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", "dev": true }, @@ -3902,36 +4157,30 @@ "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo=" }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" }, "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "version": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true }, "latest-version": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", + "version": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=", "dev": true }, "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, "lazy-req": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", + "version": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w=", "dev": true }, "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "version": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=" }, "leven": { @@ -3941,8 +4190,7 @@ "dev": true }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true }, @@ -3958,24 +4206,20 @@ "dev": true }, "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=" }, "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", + "version": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", "dev": true }, "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "version": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=" }, "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" }, "localforage": { @@ -3998,8 +4242,7 @@ } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, "lodash-es": { @@ -4008,8 +4251,7 @@ "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc=" }, "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "version": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, @@ -4026,14 +4268,12 @@ "dev": true }, "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "version": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, "lodash.cond": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "version": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", "dev": true }, @@ -4044,8 +4284,7 @@ "dev": true }, "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "version": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", "dev": true }, @@ -4096,8 +4335,7 @@ "dev": true }, "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "version": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, @@ -4131,14 +4369,12 @@ "dev": true }, "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "version": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", "dev": true }, "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "version": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", "dev": true }, @@ -4148,14 +4384,12 @@ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "version": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, "longest-streak": { @@ -4165,13 +4399,11 @@ "dev": true }, "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=" }, "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "version": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true }, @@ -4182,8 +4414,7 @@ "dev": true }, "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "version": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", "dev": true }, @@ -4194,8 +4425,7 @@ "dev": true }, "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "version": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", "dev": true }, @@ -4218,8 +4448,7 @@ "dev": true }, "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "version": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, @@ -4252,8 +4481,7 @@ "integrity": "sha512-45i+o+Tq+PAUv1CaodR+u/unGpUJroIQDuHQT7NuODLrOcscWj1JDH2o2rkEkH9qSczZ9f5Plq8v4Lj++wqALw==" }, "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "version": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, @@ -4270,19 +4498,16 @@ "dev": true }, "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "version": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" }, "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "version": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -4307,8 +4532,7 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=" }, "miller-rabin": { @@ -4317,8 +4541,7 @@ "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=" }, "mime": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "version": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=", "dev": true }, @@ -4346,8 +4569,7 @@ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=" }, "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "version": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" }, "minimalistic-crypto-utils": { @@ -4356,34 +4578,34 @@ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "version": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", "dev": true }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "optional": true + }, "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "version": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, @@ -4394,8 +4616,7 @@ "dev": true }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, @@ -4423,13 +4644,11 @@ "dev": true }, "node-libs-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "version": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", "dependencies": { "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } @@ -4441,8 +4660,7 @@ "dev": true }, "node-status-codes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "version": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", "dev": true }, @@ -4452,37 +4670,31 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==" }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=" }, "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "version": "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", + "version": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true }, "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", + "version": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true }, "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "version": "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", + "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "nwmatcher": { @@ -4498,13 +4710,11 @@ "dev": true }, "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-hash": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", + "version": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", "integrity": "sha1-KKZZz5h9lqTavnhgKJ87UybEoDw=", "dev": true }, @@ -4515,8 +4725,7 @@ "dev": true }, "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "version": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" }, "object.assign": { @@ -4532,8 +4741,7 @@ "dev": true }, "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=" }, "object.values": { @@ -4543,8 +4751,7 @@ "dev": true }, "obuf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "version": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", "integrity": "sha1-EEEktsYCxnlogaBCVB0220OlJk4=", "dev": true }, @@ -4561,14 +4768,12 @@ "dev": true }, "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true }, "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -4596,8 +4801,7 @@ "dev": true }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "dependencies": { @@ -4610,44 +4814,37 @@ } }, "original": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "version": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", "dev": true, "dependencies": { "url-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "version": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", "dev": true } } }, "os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "version": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" }, "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "version": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=" }, "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", "dev": true }, @@ -4670,14 +4867,12 @@ "dev": true }, "package-json": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", + "version": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", "dev": true }, "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "version": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" }, "paper": { @@ -4703,13 +4898,11 @@ "dev": true }, "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=" }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=" }, "parse5": { @@ -4719,14 +4912,12 @@ "dev": true }, "parseurl": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", "dev": true }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "version": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" }, "path-exists": { @@ -4735,30 +4926,25 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=" }, "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "version": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "version": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=" }, "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "version": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=" }, "pbkdf2": { @@ -4773,47 +4959,39 @@ "dev": true }, "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" }, "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "version": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true }, "pkg-up": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", + "version": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", "dev": true }, "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "version": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", "dev": true }, "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "version": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "dev": true, "dependencies": { "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -4852,459 +5030,391 @@ } }, "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "version": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "version": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "version": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "version": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "version": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "version": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "version": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "version": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "version": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-flexbugs-fixes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.0.0.tgz", + "version": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.0.0.tgz", "integrity": "sha1-ezHLbCfQQXo1pnkUwpX4PEA8ftQ=", "dev": true }, "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "version": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", "dev": true }, "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "version": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", "dev": true }, "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "version": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", "dev": true }, "postcss-loader": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.5.tgz", + "version": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.5.tgz", "integrity": "sha1-wZ0+i4PrGsMW9WIe9MDvWz0bizo=", "dev": true }, "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "version": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "version": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "version": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "dev": true, "dependencies": { "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true }, "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "version": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", "dev": true }, "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "version": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "version": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "version": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "version": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "version": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", "dev": true }, "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "version": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", "dev": true }, "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "version": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", "dev": true }, "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "version": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", "dev": true }, "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "version": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "version": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "version": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "version": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "version": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "version": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "version": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", "dev": true }, "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "version": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "version": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "version": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "version": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "dependencies": { "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true } } }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "version": "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", + "version": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "version": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" }, "pretty-bytes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "version": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", "dev": true }, "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "version": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", "dev": true }, @@ -5323,23 +5433,19 @@ } }, "private": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "version": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=" }, "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "version": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -5349,8 +5455,7 @@ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" }, "prop-types": { - "version": "15.5.10", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "version": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=" }, "proxy-addr": { @@ -5360,8 +5465,7 @@ "dev": true }, "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "version": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" }, "pseudomap": { @@ -5376,8 +5480,7 @@ "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=" }, "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "q": { @@ -5387,53 +5490,44 @@ "dev": true }, "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", "dev": true }, "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "version": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true }, "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "version": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "version": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, "querystringify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "version": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", "dev": true }, "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "version": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", "dependencies": { "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "version": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dependencies": { "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" } } }, "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "version": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=" } } @@ -5444,28 +5538,24 @@ "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==" }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", "dev": true }, "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "version": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", "dev": true, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } } }, "react": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", + "version": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=" }, "react-addons-test-utils": { @@ -5568,23 +5658,6 @@ } } }, - "react-dnd": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.5.4.tgz", - "integrity": "sha512-y9YmnusURc+3KPgvhYKvZ9oCucj51MSZWODyaeV0KFU0cquzA7dCD1g/OIYUKtNoZ+MXtacDngkdud2TklMSjw==", - "dependencies": { - "hoist-non-react-statics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", - "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=" - } - } - }, - "react-dnd-html5-backend": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.5.4.tgz", - "integrity": "sha512-jDqAkm/hI8Tl4HcsbhkBgB6HgpJR1e+ML1SbfxaegXYiuMxEVQm0FOwEH5WxUoo6fmIG4N+H0rSm59POuZOCaA==" - }, "react-docgen": { "version": "3.0.0-beta6", "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-beta6.tgz", @@ -5650,15 +5723,9 @@ } }, "react-dom": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", + "version": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=" }, - "react-dropzone": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-4.1.3.tgz", - "integrity": "sha512-AmVy1hSZ/BELkr1Pta8n+zFBfR7nZugU/hlyjauooozcBdL+d6xUMV/5xSQE/qxkKjPC7wtE/XPJUv54gLsezA==" - }, "react-error-overlay": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-1.0.9.tgz", @@ -5682,107 +5749,809 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", "dev": true - } - } - } - } - }, - "react-event-listener": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.4.5.tgz", - "integrity": "sha1-4+iVoJcM8U7o+JAROvaBl6vz0LE=" - }, - "react-group": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/react-group/-/react-group-1.0.5.tgz", - "integrity": "sha1-ygfWjLuubZklCCnhwy94JYdpPAc=", - "dev": true - }, - "react-icon-base": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.0.7.tgz", - "integrity": "sha1-C9GHNr1s55ym1pzoOHoH+41M7/4=", - "dev": true, - "dependencies": { - "prop-types": { - "version": "15.5.8", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.8.tgz", - "integrity": "sha1-a3suFBCDvjjIWVqlH8VXdccZk5Q=", - "dev": true - } - } - }, - "react-icons": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.5.tgz", - "integrity": "sha1-+UJQHCGkzARWziu+5QMsk/YFHc8=", - "dev": true - }, - "react-infinite": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/react-infinite/-/react-infinite-0.12.1.tgz", - "integrity": "sha512-sOXsm0OsszFQQ+4Vtqt1UUqLETGOCS0keAdEQuNMmeoIHHz2iIW44cHhPLxyeAsdfJQOYanmBZjhpZFQw7bhKw==", - "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-redux": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.5.tgz", - "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=" - }, - "react-router": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.1.1.tgz", - "integrity": "sha1-1Ejzt8G0Kab7sDOVCZlJxgax/pU=" - }, - "react-router-dom": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.1.1.tgz", - "integrity": "sha1-MCGt4fLBYK+Xz5TiVZTF8pRYMCU=" - }, - "react-scripts": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.7.tgz", - "integrity": "sha1-/hQ23aA7tFRlx20JfP6k8y63y7s=", - "dev": true, - "dependencies": { - "babel-core": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", - "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=", - "dev": true - }, - "babel-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.0.0.tgz", - "integrity": "sha1-LkOma+4f/0RwUz0EAsikUy+vuvc=", - "dev": true - }, - "babel-runtime": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", - "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true, - "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + } + } + } + } + }, + "react-event-listener": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.4.5.tgz", + "integrity": "sha1-4+iVoJcM8U7o+JAROvaBl6vz0LE=" + }, + "react-group": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-group/-/react-group-1.0.5.tgz", + "integrity": "sha1-ygfWjLuubZklCCnhwy94JYdpPAc=", + "dev": true + }, + "react-icon-base": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.0.7.tgz", + "integrity": "sha1-C9GHNr1s55ym1pzoOHoH+41M7/4=", + "dev": true, + "dependencies": { + "prop-types": { + "version": "15.5.8", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.8.tgz", + "integrity": "sha1-a3suFBCDvjjIWVqlH8VXdccZk5Q=", + "dev": true + } + } + }, + "react-icons": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.5.tgz", + "integrity": "sha1-+UJQHCGkzARWziu+5QMsk/YFHc8=", + "dev": true + }, + "react-infinite": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/react-infinite/-/react-infinite-0.12.1.tgz", + "integrity": "sha512-sOXsm0OsszFQQ+4Vtqt1UUqLETGOCS0keAdEQuNMmeoIHHz2iIW44cHhPLxyeAsdfJQOYanmBZjhpZFQw7bhKw==", + "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-redux": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.5.tgz", + "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=" + }, + "react-router": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.1.1.tgz", + "integrity": "sha1-1Ejzt8G0Kab7sDOVCZlJxgax/pU=" + }, + "react-router-dom": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.1.1.tgz", + "integrity": "sha1-MCGt4fLBYK+Xz5TiVZTF8pRYMCU=" + }, + "react-scripts": { + "version": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.7.tgz", + "integrity": "sha1-/hQ23aA7tFRlx20JfP6k8y63y7s=", + "dev": true, + "dependencies": { + "babel-core": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", + "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=", + "dev": true + }, + "babel-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.0.0.tgz", + "integrity": "sha1-LkOma+4f/0RwUz0EAsikUy+vuvc=", + "dev": true + }, + "babel-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", + "dev": true, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "fsevents": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.0.17.tgz", + "integrity": "sha1-hTfz8SJyZ4dltP1lKMDx9m+PRVg=", + "dev": true, + "optional": true, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.11.0", + "bundled": true, + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "commander": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "deep-extend": { + "version": "0.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "extend": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.10", + "bundled": true, + "dev": true + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.2", + "bundled": true, + "dev": true, + "optional": true + }, + "generate-function": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "generate-object-property": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "getpass": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.1", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "graceful-readlink": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "2.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-my-json-valid": { + "version": "2.15.0", + "bundled": true, + "dev": true, + "optional": true + }, + "is-property": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jsbn": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonpointer": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "optional": true + }, + "mime-db": { + "version": "1.25.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.13", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.3", + "bundled": true, + "dev": true + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true + }, + "ms": { + "version": "0.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.32", + "bundled": true, + "dev": true, + "optional": true + }, + "nopt": { + "version": "3.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npmlog": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.3.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.1.6", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "optional": true + }, + "request": { + "version": "2.79.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rimraf": { + "version": "2.5.4", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.0", + "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 + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "optional": true + }, + "sshpk": { + "version": "1.10.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "strip-json-comments": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "supports-color": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "tar-pack": { + "version": "3.3.0", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "once": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.1.5", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "bundled": true, + "dev": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, "dev": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true } } }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, "jest": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.3.tgz", @@ -5798,34 +6567,29 @@ } }, "promise": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "version": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", "dev": true }, "source-list-map": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "version": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", "integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=", "dev": true }, "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "dependencies": { "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true } } }, "webpack": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", + "version": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", "integrity": "sha1-LgRX8KuxrF3zqxBsacZy8jZ4Xwc=", "dev": true, "dependencies": { @@ -5856,20 +6620,17 @@ } }, "webpack-sources": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "version": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", "dev": true }, "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true } @@ -5988,25 +6749,27 @@ "integrity": "sha1-Am9KW7VVJmH9LMS7zQ1LyKNev34=", "dev": true }, + "react-tiny-virtual-list": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/react-tiny-virtual-list/-/react-tiny-virtual-list-2.1.4.tgz", + "integrity": "sha512-9JvkWliho5SGHlv/uz3MuObUkg60SPvw3Ottvi/n4nl7qzBvxtb9oI8kJqI9sfZYYGy7xQLbTP0vGhqtnhA04Q==" + }, "react-transition-group": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.0.tgz", "integrity": "sha1-tR/JIbDDg1p+98Vxx5/ILHPpIE8=" }, "read-all-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "version": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", "dev": true }, "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=" }, "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" }, "readable-stream": { @@ -6022,13 +6785,11 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "version": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=" }, "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true }, @@ -6045,8 +6806,7 @@ } }, "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "version": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true }, @@ -6070,34 +6830,29 @@ } }, "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "version": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true }, "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "version": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, "dependencies": { "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true } } }, "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "version": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "dev": true, "dependencies": { "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true } @@ -6116,7 +6871,8 @@ "redux-devtools-extension": { "version": "2.13.2", "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz", - "integrity": "sha1-4Pmo6N/KfBe+kscSSVijuU6ykR0=" + "integrity": "sha1-4Pmo6N/KfBe+kscSSVijuU6ykR0=", + "dev": true }, "redux-mock-store": { "version": "1.2.3", @@ -6130,8 +6886,7 @@ "integrity": "sha1-eUEgLgzgqfzAJj1mll9SY9NPQ7k=" }, "regenerate": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=", "dev": true }, @@ -6148,8 +6903,7 @@ "dev": true }, "regex-cache": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=" }, "regexpu-core": { @@ -6159,32 +6913,27 @@ "dev": true }, "registry-auth-token": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", + "version": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", "dev": true }, "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "version": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true }, "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "version": "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", + "version": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "dependencies": { "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "version": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -6215,37 +6964,31 @@ "dev": true }, "remove-trailing-separator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", + "version": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=" }, "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "version": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "dev": true, "dependencies": { "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", + "version": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", "dev": true } } }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "version": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" }, "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true }, @@ -6262,8 +7005,7 @@ "dev": true }, "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "version": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-from-string": { @@ -6273,19 +7015,16 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "version": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true }, "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "version": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, @@ -6296,8 +7035,7 @@ "dev": true }, "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "version": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true }, @@ -6307,19 +7045,16 @@ "integrity": "sha1-6DWIAbhrg7F1YNTjw4LXrvIQCUQ=" }, "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "version": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true }, "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=" }, "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true }, @@ -6329,14 +7064,12 @@ "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=" }, "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true }, "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", "dev": true }, @@ -6384,8 +7117,7 @@ "dev": true }, "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "version": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, "dependencies": { @@ -6404,8 +7136,7 @@ } }, "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "version": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, @@ -6415,8 +7146,7 @@ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "version": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true }, @@ -6441,8 +7171,7 @@ } }, "serve-index": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", + "version": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", "integrity": "sha1-0rKA/FYNYW7oG0i/D6gqvtJIXOc=", "dev": true }, @@ -6453,19 +7182,16 @@ "dev": true }, "serviceworker-cache-polyfill": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", + "version": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=", "dev": true }, "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "version": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "version": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" }, "setimmediate": { @@ -6497,8 +7223,7 @@ "dev": true }, "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true }, @@ -6509,8 +7234,7 @@ "dev": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, @@ -6520,20 +7244,17 @@ "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o=" }, "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "version": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "version": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, @@ -6544,20 +7265,17 @@ "dev": true }, "sockjs": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", + "version": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", "dev": true, "dependencies": { "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "version": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true }, "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "version": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", "dev": true } @@ -6570,20 +7288,17 @@ "dev": true }, "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "version": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true }, "source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "version": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", "dev": true }, "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" }, "source-map-support": { @@ -6614,14 +7329,12 @@ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" }, "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "version": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "dev": true }, "spdy-transport": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", + "version": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", "dev": true }, @@ -6658,8 +7371,7 @@ "dev": true }, "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "version": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=" }, "stream-cache": { @@ -6669,13 +7381,11 @@ "dev": true }, "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "version": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=" }, "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "version": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, @@ -6691,8 +7401,7 @@ "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" }, "stringify-entities": { @@ -6708,41 +7417,34 @@ "dev": true }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" }, "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=" }, "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "version": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "style-loader": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", + "version": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", "integrity": "sha1-6CVLzNt690vVgnTjYQe01atN8xA=", "dev": true }, "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" }, "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "version": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "dev": true, "dependencies": { @@ -6761,34 +7463,29 @@ } }, "sw-precache": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.0.tgz", + "version": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.0.tgz", "integrity": "sha1-62IlzlgM6q4UgZRXigrQGrfqGZw=", "dev": true }, "sw-precache-webpack-plugin": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.9.1.tgz", + "version": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.9.1.tgz", "integrity": "sha1-I4H/cG+7bKvbIKIDN96OWPtJoqc=", "dev": true, "dependencies": { "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true }, "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true } } }, "sw-toolbox": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", + "version": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=", "dev": true }, @@ -6804,8 +7501,7 @@ "dev": true }, "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "version": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "dependencies": { @@ -6816,8 +7512,7 @@ "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", + "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, @@ -6841,14 +7536,12 @@ "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" }, "test-exclude": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", + "version": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", "integrity": "sha1-TYSWSwlmsAh+zDNKLOAC09k0HiY=", "dev": true }, "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, @@ -6859,8 +7552,7 @@ "dev": true }, "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -6881,8 +7573,7 @@ } }, "timed-out": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "version": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", "dev": true }, @@ -6904,8 +7595,7 @@ "dev": true }, "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "version": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" }, "to-ast": { @@ -6929,8 +7619,7 @@ } }, "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, @@ -6940,8 +7629,7 @@ "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" }, "toposort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", + "version": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", "integrity": "sha1-8CzYp0vYvi/A6YYRw7rLlaFxhpw=", "dev": true }, @@ -6964,14 +7652,12 @@ "dev": true }, "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "version": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, @@ -6988,14 +7674,12 @@ "dev": true }, "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "version": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, "tunnel-agent": { @@ -7012,8 +7696,7 @@ "optional": true }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true }, @@ -7030,8 +7713,7 @@ "dev": true }, "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, @@ -7047,8 +7729,7 @@ "dev": true }, "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, @@ -7092,20 +7773,17 @@ "dev": true }, "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "version": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "version": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", "dev": true }, "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "version": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", "dev": true }, @@ -7151,14 +7829,12 @@ "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=" }, "unzip-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "version": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", "dev": true }, "update-notifier": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", + "version": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=", "dev": true }, @@ -7169,75 +7845,63 @@ "dev": true }, "urijs": { - "version": "1.18.10", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.18.10.tgz", + "version": "https://registry.npmjs.org/urijs/-/urijs-1.18.10.tgz", "integrity": "sha1-uURj6rpZoaeWA2pGe7YzxmfyIas=", "dev": true }, "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "version": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "dependencies": { "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "version": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" } } }, "url-loader": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", + "version": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", "integrity": "sha1-uRg7GAHg+EdxhnNnMEC8ncHHFcU=", "dev": true }, "url-parse": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", + "version": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", "dev": true, "dependencies": { "querystringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "version": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", "dev": true } } }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "version": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true }, "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true }, "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "version": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dependencies": { "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" } } }, "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "version": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", "dev": true }, @@ -7270,8 +7934,7 @@ "dev": true }, "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", + "version": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", "dev": true }, @@ -7300,8 +7963,7 @@ "dev": true }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "version": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=" }, "walker": { @@ -7327,8 +7989,7 @@ "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=" }, "wbuf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", + "version": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", "dev": true }, @@ -7392,64 +8053,54 @@ "dev": true }, "webpack-dev-server": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", + "version": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", "integrity": "sha1-MThM6BE2vhCAtLTN4OubkOVO5s8=", "dev": true, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "version": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true }, "opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "version": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", "dev": true }, "sockjs-client": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", + "version": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", "integrity": "sha1-8CEqhVDkyUaMjM6u79LjSTwDOtU=", "dev": true }, "yargs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "version": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true }, "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true } } }, "webpack-manifest-plugin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz", + "version": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz", "integrity": "sha1-a2xxiq3oolN5lXhLRr0umDYFfKo=", "dev": true, "dependencies": { "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", "dev": true }, "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true } @@ -7462,8 +8113,7 @@ "dev": true }, "webpack-sources": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "version": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", "dev": true }, @@ -7474,14 +8124,12 @@ "dev": true }, "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "version": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", "dev": true }, "websocket-extensions": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "version": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", "dev": true }, @@ -7500,8 +8148,7 @@ } }, "whatwg-fetch": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "version": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" }, "whatwg-url": { @@ -7519,8 +8166,7 @@ } }, "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "version": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", "dev": true }, @@ -7531,19 +8177,16 @@ "dev": true }, "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "version": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" }, "widest-line": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", + "version": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", "dev": true }, "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, "wordwrap": { @@ -7558,25 +8201,21 @@ "dev": true }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "version": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=" }, "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "version": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true }, "write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "version": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true }, @@ -7593,8 +8232,7 @@ "dev": true }, "xdg-basedir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "version": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", "dev": true }, @@ -7617,13 +8255,11 @@ "dev": true }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "version": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, "yallist": { diff --git a/viscoll-app/package.json b/viscoll-app/package.json index 5020f0f1..a86618b8 100644 --- a/viscoll-app/package.json +++ b/viscoll-app/package.json @@ -17,20 +17,18 @@ "openseadragon": "^2.3.1", "paper": "^0.11.4", "react": "^15.6.1", - "react-dnd": "^2.5.4", - "react-dnd-html5-backend": "^2.5.4", "react-dom": "^15.6.1", - "react-dropzone": "^4.1.3", "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", "redux": "^3.7.1", "redux-axios-middleware": "^4.0.0", - "redux-devtools-extension": "^2.13.2", "redux-persist": "^4.8.2", "webpack": "^3.0.0" }, "devDependencies": { + "redux-devtools-extension": "^2.13.2", "babel-jest": "^20.0.3", "babel-loader": "^7.1.1", "babel-preset-es2015": "^6.24.1", diff --git a/viscoll-app/sass/components/_dialog.scss b/viscoll-app/sass/components/_dialog.scss index dec30f2d..36a7b171 100644 --- a/viscoll-app/sass/components/_dialog.scss +++ b/viscoll-app/sass/components/_dialog.scss @@ -26,48 +26,73 @@ } } .feedbackDialog { + p { + color: $black; + } .label { + color: $black; width: 100px; display:inline-block; vertical-align: top; } .input { - width: 200px; + width: 250px; display:inline-block; text-align: right; } } .newProjectDialog { + p { + color: $black; + } h1 { font-weight: normal; text-transform: inherit; } + h2 { + font-size: 1em; + } + .section { + margin-left: 2em; + } .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: 1.9em 0em; - @include transition(all, 200ms, ease-in-out); - border: 2px solid $white; - - &:hover { - border: 2px solid $teal; - cursor: pointer; - } + padding: 2.5em 0em; .icon { width: 50px; padding:0px 15px; } .text { - h1 { - font-size: 2em; - margin:0; + line-height: 2.5em; + span:nth-child(1) { + text-align: left; + font-size: 2.5em; + display: block; + color: $black; + width: 100%; } - h2 { - font-size: 1em; - color: lighten($black, 25); - padding-top:5px; - margin:0; + span:nth-child(2) { + font-size: 1.3em; + color: lighten($black, 15); + width: 100%; + display: block; } } } 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/index.scss b/viscoll-app/sass/index.scss index c0127dd6..1cb8539c 100644 --- a/viscoll-app/sass/index.scss +++ b/viscoll-app/sass/index.scss @@ -11,9 +11,11 @@ @import 'layout/filter'; @import 'layout/loading'; @import 'layout/404'; +@import 'layout/dashboard'; @import 'layout/imageManager'; @import 'components/dialog'; @import 'components/tooltip'; +@import 'components/textarea'; @import 'typography'; html, body { 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/_imageManager.scss b/viscoll-app/sass/layout/_imageManager.scss index 288c2856..2d634f50 100644 --- a/viscoll-app/sass/layout/_imageManager.scss +++ b/viscoll-app/sass/layout/_imageManager.scss @@ -23,7 +23,7 @@ span { font-size: 0.8em; font-weight: normal; - color: transparentize($black, 0.4); + color: transparentize($black, 0.2); } &>div { flex-grow: 1; @@ -40,16 +40,16 @@ border-width: 0px 1px 0px 1px; border-style: solid; border-color: $gray; - cursor: move; + cursor: pointer; @include border-radius(3px); padding-left: 0.5em; - + display: flex; + justify-content: space-between; + align-items: center; .text { - display: inline-block; color: $black; padding-left: 0.5em; - @include centerize(0%, 50%); &>span { font-size: 0.8em; color: transparentize($black, 0.3); @@ -57,8 +57,6 @@ } .thumbnail { opacity: 0.5; - display: inline-block; - width: 1.5em; @include transition(all, 200ms, ease-in-out); &:hover { @@ -68,6 +66,14 @@ } } + .middleBar { + display: flex; + justify-content: space-around; + background: $white; + margin: 0.5em 1em; + padding: 0.2em 0em; + } + .panelBar { background: $white; width: 100%; @@ -91,7 +97,6 @@ display: flex; flex-direction: column; width: 97%; - height: 40vh; margin-top: 1em; margin-left: 1em; background: darken($gray, 3); @@ -103,10 +108,6 @@ margin: 0em; // padding: 0.5em; } - .panelBarGroup { - height: 50px; - display: flex; - } .boards { display: flex; width: 100%; @@ -116,9 +117,13 @@ } } .binText { - @include centerize(50%,50%); text-transform: uppercase; text-align: center; + display:flex; + align-items: center; + justify-content: center; + width: 100% !important; + height: 38vh; } } .bottomPanel { @@ -127,9 +132,7 @@ justify-content: space-between; width: 97%; // height: 42vh; - margin-top: 1em; margin-left: 1em; - // background: red; .backlog { width: 49%; padding: 0em; @@ -174,14 +177,16 @@ } } .mainToolbar { - position: fixed; - bottom: 0; + // 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)); + @include box-shadow(0px 2px 2px 0px rgba(0,0,0,0.05)); .message { padding-left: 1em; text-transform: uppercase; @@ -189,10 +194,7 @@ font-size: 0.9em; } .actions { - text-align: right; - } - &>div { - width: 40%; + padding-right:1em; } } } diff --git a/viscoll-app/sass/layout/_infobox.scss b/viscoll-app/sass/layout/_infobox.scss index dae95bd1..52574517 100644 --- a/viscoll-app/sass/layout/_infobox.scss +++ b/viscoll-app/sass/layout/_infobox.scss @@ -27,4 +27,16 @@ width: 55%; } } + 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/_notes.scss b/viscoll-app/sass/layout/_notes.scss index 2d27efc1..4c563e37 100644 --- a/viscoll-app/sass/layout/_notes.scss +++ b/viscoll-app/sass/layout/_notes.scss @@ -3,35 +3,15 @@ .container { padding: 1em 2em 0em 2em; } - .browse { height: 100%; .notesList { + text-align: center; .item { - margin: 1em; - padding: 1em; - display: block; - background: $gray; - cursor: pointer; + margin: 0.5em 0em 0em 0em; + overflow: hidden; @include transition(background, 200ms, ease-in-out); - - &:hover { - background: lighten($gray, 4); - } - &.active { - font-weight: 600; - background: $teal; - } - &.add { - border: 1px solid lighten($success, 30); - background: lighten($gray, 10); - - &.active { - background: $success; - color: $white; - } - } } } .details { diff --git a/viscoll-app/sass/layout/_sidebar.scss b/viscoll-app/sass/layout/_sidebar.scss index 73232cb1..26d38650 100644 --- a/viscoll-app/sass/layout/_sidebar.scss +++ b/viscoll-app/sass/layout/_sidebar.scss @@ -3,11 +3,12 @@ display: block; top: 55px; width: 18%; - height: 100%; + height: 99%; background: $bg_blue; opacity: 1; @include transition(all, 200ms, ease-in-out); - + overflow-y: auto; + &.hidden { opacity: 0; } @@ -18,7 +19,32 @@ h1 { 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; + } + } + .panel { + .header { + padding: 0.5em 1em; + display: flex; + justify-content: space-between; + } + .content { + padding: 1em; + background: transparentize($fg_blue, 0.8); + &.hidden { + display: none; + } + } } .selectMode { padding: 1em 1em 2em 1em; @@ -45,32 +71,32 @@ .manager { text-align: center; padding: 0.5em 0em; - cursor: pointer; - margin: 0px -16px; + 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($white,0.98); + background: transparentize($teal,0.90); font-weight: bold; } &.active { - background: transparentize($white,0.95); + background: $teal; font-weight: bold; - border-left: 3px solid $teal; + color: $black; } } .export { - div + div { - margin-top: 10px; - } + line-height: 45px; } } - - .feedback { position: fixed; bottom: 0; diff --git a/viscoll-app/sass/layout/_tabular.scss b/viscoll-app/sass/layout/_tabular.scss index f445467c..d66ed4b7 100644 --- a/viscoll-app/sass/layout/_tabular.scss +++ b/viscoll-app/sass/layout/_tabular.scss @@ -1,31 +1,72 @@ +.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 { - margin-top: -21px; - @include transition(background, 100ms, ease-in-out); - &:hover { - background: lighten($teal, 30) !important; - cursor: pointer; + justify-content: space-between; + + .groupSection { + display: flex; } - &.active:hover { - background: $teal !important; + + .toggleButton { + float: right; } } } -.leafSection { - display: flex; - flex-grow: 1; - border: 1px solid $white; - @include transition(background, 100ms, ease-in-out); - &:hover { - background: lighten($teal, 30); - cursor: pointer; - } - &.active:hover { - background: $teal !important; +.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; @@ -81,17 +122,23 @@ 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: 1px solid $white; - - &:hover { - background: lighten($teal, 30); - cursor: pointer; + border: 2px solid $white; + cursor: pointer; + + &.active { + background: $teal; + } + &.focus { + border: 2px solid $teal; + color: transparentize($black, 0); } + .name { width: 40px; display: inline-block; @@ -122,28 +169,36 @@ } } &:first-child { - border-bottom: 1px solid transparentize($black, 0.85); + border-bottom: 2px solid transparentize($black, 0.90); + &.focus { + border-bottom: 2px solid $teal; + } } } } .sideToggle { width: 20px; - height: 44px; + height: 49px; border-left: 1px solid transparentize($black, 0.85); + cursor: pointer; .side { display: block; - width: 20px; - height: 20px; - padding-top: 2px; - padding-left: 5px; + 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); } - &:hover { - background: lighten($teal, 20); - cursor: pointer; + &.active { + background: $teal; + } + &.focus { + border: 2px solid $teal; color: transparentize($black, 0); } } diff --git a/viscoll-app/sass/lib/_variables.scss b/viscoll-app/sass/lib/_variables.scss index d7684a94..a0598287 100644 --- a/viscoll-app/sass/lib/_variables.scss +++ b/viscoll-app/sass/lib/_variables.scss @@ -7,7 +7,7 @@ $white: #FFFFFF; $gray: #F2F2F2; $dark_gray: #727272; $black: #4e4e4e; -$error: #D87979; +$error: #bd4a4a; $success: #34A251; $warning: #E37A05; diff --git a/viscoll-app/src/actions/editCollation/interactionActions.js b/viscoll-app/src/actions/editCollation/interactionActions.js index 56b94072..d630f91e 100644 --- a/viscoll-app/src/actions/editCollation/interactionActions.js +++ b/viscoll-app/src/actions/editCollation/interactionActions.js @@ -33,6 +33,18 @@ export function toggleFilterPanel(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)) { @@ -63,9 +75,7 @@ export function handleObjectClick(selectedObjects, object, event, objects) { } } if (event.shiftKey || (event.modifiers!==undefined && event.modifiers.shift)) { - try {event.preventDefault()} - catch (e) {}; - + window.getSelection().removeAllRanges(); // Object type changed, clear all active selected objects if (selectedObjects.type !== object.memberType) { selectedObjects.members = [object.id]; @@ -297,9 +307,9 @@ export function updateFilterSelection(selection, matchingFilterObjects, allObjec } -export function toggleTacket(request) { +export function toggleVisualizationDrawing(request) { return { - type: 'TOGGLE_TACKET', + type: 'TOGGLE_VISUALIZATION_DRAWING', payload: request }; -} +} \ No newline at end of file diff --git a/viscoll-app/src/actions/editCollation/modificationActions.js b/viscoll-app/src/actions/editCollation/modificationActions.js index 6f554d5d..38b47942 100644 --- a/viscoll-app/src/actions/editCollation/modificationActions.js +++ b/viscoll-app/src/actions/editCollation/modificationActions.js @@ -394,32 +394,15 @@ export function deleteNoteType(noteType) { } -export function mapSidesToImages(linkedSideIDs, images, unlinkedSideIDs) { - // linkedSideIDs = [{id, ...}, {id, ...}] - // images = [{manifestID: 123, label: "imageName", url: "http..."}] - // unlinkedSideIDs = [id, id, id] - let sides = []; - for (const index of linkedSideIDs.keys()) { - let side = {id: linkedSideIDs[index].id}; - side["attributes"] = { - image: {manifestID: images[index].manifestID, label: images[index].id, url: images[index].url} - } - sides.push(side); - } - for (const id of unlinkedSideIDs) { - let side = {id}; - side["attributes"] = { - image: {} - } - sides.push(side); - } +export function mapSidesToImages(sideMappings) { + // sideMappings = [{id: 112, image: {}}, ...] return { types: ['SHOW_LOADING','MAP_SIDES_SUCCESS','MAP_SIDES_FAILED'], payload: { request : { url: `/sides`, method: 'put', - data: {sides}, + data: {sides: sideMappings}, successMessage: "Successfully updated the sides" , errorMessage: "Ooops! Something went wrong" } diff --git a/viscoll-app/src/actions/userActions.js b/viscoll-app/src/actions/userActions.js index d4d5ce01..203340c2 100644 --- a/viscoll-app/src/actions/userActions.js +++ b/viscoll-app/src/actions/userActions.js @@ -136,14 +136,14 @@ export function deleteProfile(userID) { }; } -export function sendFeedback(title, message) { +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}, + data: {title, message, browserInformation, project}, successMessage: "You have successfully sent a feedback!", errorMessage: "Ooops! Something went wrong" } diff --git a/viscoll-app/src/assets/visualMode/PaperLeaf.js b/viscoll-app/src/assets/visualMode/PaperLeaf.js index 1e5cb87b..f8190a68 100644 --- a/viscoll-app/src/assets/visualMode/PaperLeaf.js +++ b/viscoll-app/src/assets/visualMode/PaperLeaf.js @@ -58,31 +58,81 @@ PaperLeaf.prototype = { this.showAttributes(); this.createAttachments(); - let notesToShow = this.leaf.notes.filter((noteID)=>{return this.Notes[noteID].show}); - for (let noteIndex = 0; noteIndex < notesToShow.length; noteIndex++) { - // Draw note text - let x = 0; - let fontSize = this.spacing*0.40; - let numChars = this.path.bounds.width/fontSize*2.4; - if (this.attachment.bounds.width>0) { - // This leaf has an attachment (glue, etc) - // Place text to the right of attachment drawing - x = this.attachment.bounds.right+5; - } else if (this.isConjoined()) { - // This leaf is conjoined - x = this.path.segments[1].point.x; - } else { - // Separate leaf - x = this.path.segments[0].point.x + 10; - } + const leafNotesToShow = this.leaf.notes.filter((noteID)=>{return this.Notes[noteID].show}); + const rectoNotesToShow = this.recto.notes.filter((noteID)=>{return this.Notes[noteID].show}); + 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 + 10; + } + if (this.leaf.attached_above.includes("Partial")) { + // This leaf has a partial glue attachment + // Place text to the right of attachment drawing + textX = this.attachment.bounds.right+5; + } else if (this.leaf.attached_above.includes("Glued")) { + // Other type of glueing exists + textY -= this.spacing*0.7; + } + + let that = this; + // Draw recto note text + for (let noteIndex = 0; noteIndex < rectoNotesToShow.length; noteIndex++) { + const note = this.Notes[rectoNotesToShow[noteIndex]]; + let textNote = new paper.PointText({ + content: "â–¼ " + this.recto.folio_number + " : " + note.title.substr(0,numChars), + point: [textX, textY - noteIndex*(this.spacing*0.7) - this.spacing*0.3], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + textNote.onClick = function(event) { + that.openNoteDialog(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.leaf.order + " : " + note.title.substr(0,numChars), + point: [textX, textY - rectoNotesToShow.length*(this.spacing*0.7) - noteIndex*(this.spacing*0.7) - this.spacing*0.3], + fillColor: this.strokeColor, + fontSize: fontSize, + }); + textNote.onClick = function(event) { + that.openNoteDialog(note); + }; + this.textNotes.addChild(textNote); + } + // Draw verso note text + for (let noteIndex = 0; noteIndex < versoNotesToShow.length; noteIndex++) { + const note = this.Notes[versoNotesToShow[noteIndex]]; let textNote = new paper.PointText({ - content: this.Notes[notesToShow[noteIndex]].title.substr(0,numChars), - point: [x, this.y - noteIndex*(this.spacing*0.7) - 10], + content: "â–² " + this.verso.folio_number + " : " + note.title.substr(0,numChars), + point: [textX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8], fillColor: this.strokeColor, fontSize: fontSize, }); + textNote.onClick = function(event) { + that.openNoteDialog(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() { @@ -141,7 +191,7 @@ PaperLeaf.prototype = { this.textVerso.onMouseLeave = function(event) {}; }, createAttachments: function() { - if (this.order>1 && this.leaf.attached_above==="Glued") { + if (this.order>1 && this.leaf.attached_above.includes("Glued")) { this.createGlue(); } else if (this.order>1 && this.leaf.attached_above==="Other") { this.createOtherAttachment(); @@ -159,16 +209,54 @@ PaperLeaf.prototype = { x = this.prevPaperLeaf().path.segments[0].point.x; } } - for (let i=0; i<6; i++) { - let glueLine = new paper.Path(); - glueLine.add(new paper.Point(x, this.y-10)); - glueLine.add(new paper.Point(x+10, this.y-20)); - glueLine.strokeColor = "#707070"; - glueLine.strokeWidth = 2; - this.attachment.addChild(glueLine); - x += 5; + if (this.leaf.attached_above.includes("Partial")) { + for (let i=0; i<6; i++) { + 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 if (this.leaf.attached_above.includes("Drumming")) { + let glueLineCount = 15; + if (this.leaf.stub!=="None") glueLineCount = 4; + // Draw left drum glue + for (let i=0; ileaf.removeMouseEventHandlers()); document.body.style.cursor = "crosshair"; - this.drawTacketGuide(groupID); + this.drawTacketGuide(groupID, type); this.tool.onMouseDown = (event) => { this.tacketLineDrag = new paper.Path(); @@ -167,13 +169,19 @@ PaperManager.prototype = { this.paperLeaves.forEach((leaf)=> { leaf.deactivate(); }); - this.toggleTacket(""); + this.toggleVisualizationDrawing({type: type, value: ""}); if (targets.length>0) { - let targetLeaf = targets[targets.length/2]; - this.addTacket(targetLeaf.leaf.parentID, targetLeaf.leaf.id); + 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 tacket - this.drawTackets(); + // Redraw old visualization + if (type==="tacketed") { + this.drawTackets(); + } else { + this.drawSewing(); + } } } this.tool.onMouseDrag = (event) => { @@ -203,7 +211,7 @@ PaperManager.prototype = { }); } }, - drawTacketGuide: function(groupID) { + drawTacketGuide: function(groupID, type) { const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); const guideY = targetGroup.path.bounds.height/2; const guideX = targetGroup.path.bounds.left; @@ -230,8 +238,9 @@ PaperManager.prototype = { guideLineX2.add(new paper.Point(guideX-10, guideLine.segments[0].point.y+10)); guideLineX2.add(new paper.Point(guideX+10, guideLine.segments[0].point.y-10)); + const drawType = type==="tacketed"? "TACKET" : "SEWING"; let guideText = new paper.PointText({ - content: "DRAW TACKET LINE", + content: "DRAW " + drawType + " LINE", point: [guideX+20, targetGroup.path.bounds.y + guideY - 20], fillColor: "#000000", fontSize: 12, @@ -261,25 +270,93 @@ PaperManager.prototype = { this.paperGroups.forEach((group)=>group.setMouseEventHandlers()); this.paperLeaves.forEach((leaf)=>leaf.setMouseEventHandlers()); }, + drawSewing: function() { + this.paperGroups.forEach((group)=> { + 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) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.Leafs[leafID1].order); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.Leafs[leafID2].order); + startX = paperLeaf1.path.segments[0].point.x-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+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)=> { if (group.group.tacketed!==null && group.group.tacketed.length>0) { - const targetLeafMemberID = group.group.memberIDs.find((memberID)=>{return (memberID.charAt(0)==="L"&& memberID===group.group.tacketed)}); - if (targetLeafMemberID!==undefined) { - const paperLeaf = this.getLeaf(this.Leafs[targetLeafMemberID].order); + const leafID1 = group.group.tacketed[0]; + const leafID2 =group.group.tacketed[1]; + if (leafID1!==undefined) { + let startX, startY, endX, endY; + let paperLeaf1, paperLeaf2; + + paperLeaf1 = this.getLeaf(this.Leafs[leafID1].order); + if (leafID2!==undefined) { + paperLeaf2 = this.getLeaf(this.Leafs[leafID2].order); + startX = paperLeaf1.path.segments[0].point.x-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(15, paperLeaf.path.segments[0].point.y-2)); - tacketPath1.add(new paper.Point(paperLeaf.path.segments[0].point.x+this.strokeWidth, paperLeaf.path.segments[0].point.y-2)); + tacketPath1.add(new paper.Point(startX, startY-2)); + tacketPath1.add(new paper.Point(endX+this.strokeWidth, endY-2)); tacketPath1.add(new paper.Point(tacketPath1.segments[1].point.x+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(15, paperLeaf.path.segments[0].point.y+2)); - tacketPath2.add(new paper.Point(paperLeaf.path.segments[0].point.x+this.strokeWidth, paperLeaf.path.segments[0].point.y+2)); + tacketPath2.add(new paper.Point(startX, startY+2)); + tacketPath2.add(new paper.Point(endX+this.strokeWidth, endY+2)); tacketPath2.add(new paper.Point(tacketPath2.segments[1].point.x+5, tacketPath2.segments[1].point.y+3)); const that = this; // Add listeners @@ -380,29 +457,43 @@ PaperManager.prototype = { } members.forEach((memberID, i)=> { let memberObject = this[memberID.split("_")[0]+"s"][memberID]; - let notesToShow = memberObject.notes.filter((noteID)=>{return this.Notes[noteID].show}); + 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" && memberObject.memberOrder===1 && notesToShow.length>0) { + if (memberObject.memberType==="Leaf" && memberObject.memberOrder===1 && notesToShowAbove>0) { // First leaf in the group with a note this.multipliers[memberObject.order] = multiplier; - currentY = currentY + spacing*(notesToShow.length+1); + currentY = currentY + spacing*(notesToShowAbove+1); if (i > 0 && members[i-1].memberType==="Group" && members[i-1].memberIDs.length) { // Previous sibling is a group with children currentY = currentY - memberObject.nestLevel*spacing; } 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" && memberObject.order > 0) { this.multipliers[memberObject.order] = multiplier; - currentY = currentY + spacing*(Math.max(1,notesToShow.length)); + currentY = currentY + spacing*(Math.max(1,notesToShowAbove)) + spacing*glueSpacing; if (i > 0 && members[i-1].memberType==="Group" && this.Groups[members[i-1]].memberIDs.length) { // Previous sibling is a group with children currentY = currentY - memberObject.nestLevel*spacing; } this.leafYs.push(currentY); + + currentY = currentY + spacing*notesToShowBelow*0.8; + if (i===members.length-1) { // Last member of group currentY = currentY + (memberObject.nestLevel)*spacing/4; @@ -524,6 +615,10 @@ PaperManager.prototype = { 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); @@ -572,11 +667,13 @@ function PaperManager(args) { this.groupTacketGuide = new paper.Group(); this.groupTacketGuideLine = new paper.Group(); this.groupTacket = new paper.Group(); - this.toggleTacket = args.toggleTacket; - this.addTacket = args.addTacket; + this.toggleVisualizationDrawing = args.toggleVisualizationDrawing; + this.addVisualization = args.addVisualization; this.tacketToolIsActive = false; this.tacketToolOriginalPosition = 0; this.slideForward = true; + this.openNoteDialog = args.openNoteDialog; + let that = this; // Flash newly added items paper.view.onFrame = function(event) { diff --git a/viscoll-app/src/components/authentication/Login.js b/viscoll-app/src/components/authentication/Login.js index b05bbad0..93a4375a 100644 --- a/viscoll-app/src/components/authentication/Login.js +++ b/viscoll-app/src/components/authentication/Login.js @@ -3,8 +3,9 @@ import PropTypes from 'prop-types'; import ResendConfirmation from './ResendConfirmation'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; -import { btnLg } from '../../styles/button'; -import floatFieldDark from '../../styles/textfield'; +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`. */ @@ -44,7 +45,7 @@ class Login extends Component { * @public */ submit = (e) => { - e.preventDefault(); + if (e) e.preventDefault(); this.setState({ error: ""}); this.props.action.loginUser({email: this.state.email, password: this.state.password}); } @@ -67,17 +68,25 @@ class Login extends Component { fullWidth {...btnLg} /> - let content =
+ let content =

{this.state.error}

- this.onInputChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} /> - this.onInputChange(v,"password")} name="password" type="password" floatingLabelText="Password" {...floatFieldDark} /> + 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()} + />
-

- Forgot password? + ; if (this.state.error && this.state.error.includes("unconfirmed")) { content = { - e.preventDefault(); + if (e) e.preventDefault(); this.props.action.registerUser({...this.state}); } @@ -45,23 +46,8 @@ class Register extends Component { registerSuccess = this.props.userState.registerSuccess; } catch (e) {} - let cancelMessage = "Cancel"; - if (this.props.user && this.props.user.registerSuccess) { - cancelMessage = "Okay"; - } - let cancel = ( -
- -
- ); let registerForm = ( -
+ this.onInputChange(v, "name")} name="name" @@ -75,6 +61,8 @@ class Register extends Component { floatingLabelText="E-mail" {...floatFieldDark} errorText={emailError} + aria-invalid={emailError && emailError.length>0} + aria-required={true} /> 0} + aria-required={true} />

@@ -95,14 +85,28 @@ class Register extends Component { type="submit" name="submit" /> - {cancel} +
+ this.props.tapCancel()} + label="Go back" + {...btnAuthCancel} + /> +
); const successMessage = ( -

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

+
+

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

+
+ this.props.tapCancel()} + label="Okay" + /> +
); if (registerSuccess) { diff --git a/viscoll-app/src/components/authentication/ResendConfirmation.js b/viscoll-app/src/components/authentication/ResendConfirmation.js index a926d55e..642b62c6 100644 --- a/viscoll-app/src/components/authentication/ResendConfirmation.js +++ b/viscoll-app/src/components/authentication/ResendConfirmation.js @@ -1,8 +1,9 @@ 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'; +import FlatButton from 'material-ui/FlatButton'; +import { btnLg, btnAuthCancel } from '../../styles/button'; +import {floatFieldDark} from '../../styles/textfield'; /** * Resend confirmation form. @@ -38,7 +39,7 @@ class ResendConfirmation extends Component { render() { - let form = this.state.submitted? "":
+ 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}
); } diff --git a/viscoll-app/src/components/authentication/ResetPassword.js b/viscoll-app/src/components/authentication/ResetPassword.js index 71259f64..fbb2445c 100644 --- a/viscoll-app/src/components/authentication/ResetPassword.js +++ b/viscoll-app/src/components/authentication/ResetPassword.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import { btnLg } from '../../styles/button'; -import floatFieldDark from '../../styles/textfield'; +import {floatFieldDark} from '../../styles/textfield'; /** * Contains the form to update password when user forgets password. */ @@ -32,7 +32,7 @@ class ResetPassword extends Component { * @public */ submit = (e) => { - e.preventDefault(); + if (e) e.preventDefault(); let resetMessage = "" if (!this.state.password || !this.state.passwordConfirm) { resetMessage = "Error: Both Password & Password Confirmation must be filled"; @@ -54,7 +54,7 @@ class ResetPassword extends Component { 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} /> @@ -66,6 +66,7 @@ class ResetPassword extends Component { type="submit" name="submit" {...btnLg} + onClick={() => this.submit(null)} /> ); diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.js b/viscoll-app/src/components/authentication/ResetPasswordRequest.js index 77913218..479ec152 100644 --- a/viscoll-app/src/components/authentication/ResetPasswordRequest.js +++ b/viscoll-app/src/components/authentication/ResetPasswordRequest.js @@ -2,8 +2,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; -import { btnLg, btnMd } from '../../styles/button'; -import floatFieldDark from '../../styles/textfield'; +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 @@ -35,7 +36,7 @@ class ResetPasswordRequest extends Component { * @public */ resetPasswordRequest = (e) => { - e.preventDefault(); + if (e) e.preventDefault(); let re = /[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}/igm; let resetMessage = "" if (!this.state.email) { @@ -52,29 +53,40 @@ class ResetPasswordRequest extends Component { }; render() { - let cancelMessage = "Cancel"; + let cancelButton = this.props.tapCancel()} + label="Go back" + {...btnAuthCancel} + />; let submit =
+ {...btnLg} + onClick={() => this.resetPasswordRequest(null)} + />
; if (this.state.requested) { - cancelMessage = "Okay"; + cancelButton = this.props.tapCancel()} + label="Okay" + {...btnLg} + />; submit = ""; } return ( -
+

{this.state.resetMessage}

this.onInputChange(v,"email")} name="email" floatingLabelText="E-mail" {...floatFieldDark} /> { submit }
- + { cancelButton }
) diff --git a/viscoll-app/src/components/collationManager/TabularMode.js b/viscoll-app/src/components/collationManager/TabularMode.js index 5f731069..ddcb646e 100644 --- a/viscoll-app/src/components/collationManager/TabularMode.js +++ b/viscoll-app/src/components/collationManager/TabularMode.js @@ -1,37 +1,68 @@ import React from 'react'; +import update from 'immutability-helper'; import PropTypes from 'prop-types'; import Group from './tabularMode/Group'; /** Stateless functional component that mounts the root groups. */ -const TabularMode = (props) => { - const { filters } = props.collationManager - const { Groups, groupIDs } = props.project - let group_components = []; - for (let groupID of groupIDs){ - const group = Groups[groupID] - if (group.nestLevel === 1) - group_components.push( - - ); +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]]}); } - let emptyResults = false; - const activeFiltersLength = filters.Groups.length + filters.Leafs.length + filters.Sides.length + filters.Notes.length; - if (activeFiltersLength===0) - emptyResults = true && filters.hideOthers && filters.active + 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} + /> + ); + } - return ( -
- {emptyResults ?

No objects match the query

: group_components} -
- ); + 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} +
+ ); + } } TabularMode.propTypes = { @@ -39,4 +70,3 @@ TabularMode.propTypes = { handleObjectClick: PropTypes.func, } -export default TabularMode; diff --git a/viscoll-app/src/components/collationManager/ViewingMode.js b/viscoll-app/src/components/collationManager/ViewingMode.js index bb550446..0897908e 100644 --- a/viscoll-app/src/components/collationManager/ViewingMode.js +++ b/viscoll-app/src/components/collationManager/ViewingMode.js @@ -17,8 +17,8 @@ export default class ViewingMode extends React.Component { paperManager: new PaperManager({ canvasID: 'myCanvas', origin: 0, - spacing: 0.06, - strokeWidth: 0.016, + spacing: 0.04, + strokeWidth: 0.015, strokeColor: 'rgb(82,108,145)', strokeColorActive: 'rgb(78,214,203)', strokeColorGroupActive: 'rgb(82,108,145)', @@ -55,11 +55,13 @@ export default class ViewingMode extends React.Component { this.props.collationManager.selectedObjects!==nextProps.collationManager.selectedObjects || this.props.collationManager.filters !== nextProps.collationManager.filters || this.props.collationManager.visibleAttributes !== nextProps.collationManager.visibleAttributes || - this.props.project.Notes!==nextProps.project.Notes + this.props.project.Notes!==nextProps.project.Notes || + this.state.viewingMode !== nextState.viewingMode || + this.props.imageViewerEnabled !== nextProps.imageViewerEnabled ); } - componentWillUpdate(nextProps) { + 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 : []); @@ -68,10 +70,13 @@ export default class ViewingMode extends React.Component { 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.collationManager.visibleAttributes); - this.drawOnCanvas(); } } + componentDidUpdate() { + this.drawOnCanvas(); + } + componentWillUnmount() { window.removeEventListener("resize", this.drawOnCanvas); } @@ -85,16 +90,23 @@ export default class ViewingMode extends React.Component { this.updateCanvasSize(); this.state.paperManager.draw(); } - /** * Update canvas size based on current window size * @public */ updateCanvasSize = () => { // Resize the canvas - let maxWidth = window.innerWidth-window.innerWidth*0.75; + 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); + } } @@ -102,7 +114,7 @@ export default class ViewingMode extends React.Component { let canvasAttr = { 'data-paper-hidpi': 'off', 'height': "99999999px", - 'width': window.innerWidth-window.innerWidth*0.75, + 'width': this.props.imageViewerEnabled? window.innerWidth-window.innerWidth*0.75: window.innerWidth-window.innerWidth*0.46, }; @@ -124,10 +136,14 @@ export default class ViewingMode extends React.Component { return (
-
+
+
- + {this.props.imageViewerEnabled? + + :"" + }
); } diff --git a/viscoll-app/src/components/collationManager/VisualMode.js b/viscoll-app/src/components/collationManager/VisualMode.js index b199c5cc..1a9b043a 100644 --- a/viscoll-app/src/components/collationManager/VisualMode.js +++ b/viscoll-app/src/components/collationManager/VisualMode.js @@ -12,7 +12,8 @@ export default class VisualMode extends React.Component { } componentDidMount() { - this.props.toggleTacket(""); + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); window.addEventListener("resize", this.drawOnCanvas); this.setState({ paperManager: new PaperManager({ @@ -44,13 +45,15 @@ export default class VisualMode extends React.Component { flashItems: this.props.collationManager.flashItems, filters: this.props.collationManager.filters, visibleAttributes: this.props.collationManager.visibleAttributes, - toggleTacket: this.props.toggleTacket, - addTacket: this.addTacket, + toggleVisualizationDrawing: this.props.toggleVisualizationDrawing, + addVisualization: this.addVisualization, + openNoteDialog: this.props.openNoteDialog, }) }, ()=>{this.drawOnCanvas();}); } componentWillUnmount() { - this.props.toggleTacket(""); + this.props.toggleVisualizationDrawing({type:"tacketed", value: ""}); + this.props.toggleVisualizationDrawing({type:"sewing", value: ""}); this.state.paperManager.deactivateTacketTool(); window.removeEventListener("resize", this.drawOnCanvas); } @@ -65,7 +68,8 @@ export default class VisualMode extends React.Component { this.props.collationManager.flashItems !== nextProps.collationManager.flashItems || this.props.collationManager.filters !== nextProps.collationManager.filters || this.props.collationManager.visibleAttributes !== nextProps.collationManager.visibleAttributes || - this.props.tacketing !== nextProps.tacketing + this.props.tacketed !== nextProps.tacketed || + this.props.sewing !== nextProps.sewing ); } @@ -80,18 +84,19 @@ export default class VisualMode extends React.Component { this.state.paperManager.setFilter(nextProps.collationManager.filters); this.state.paperManager.setVisibility(nextProps.collationManager.visibleAttributes); this.drawOnCanvas(); - if (nextProps.tacketing!=="") { - this.state.paperManager.activateTacketTool(nextProps.tacketing); + 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(); } } } - - addTacket = (groupID, leafID) => { + addVisualization = (groupID, type, leafIDs) => { let updatedGroup = { - tacketed: leafID, + [type]: leafIDs, } this.props.updateGroup(groupID, updatedGroup); } 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..48cd289d --- /dev/null +++ b/viscoll-app/src/components/collationManager/dialog/NoteDialog.js @@ -0,0 +1,113 @@ +import React from 'react'; +import EditNoteForm from '../../notesManager/EditNoteForm'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; + +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.Groups[value].order}`; + 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.Leafs[value].order}`; + 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.Leafs[this.props.Rectos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Recto}`; + sidesWithCurrentNote.push({label, value}) + } + for (let value of versosWithCurrentNote){ + const leafOrder = this.props.Leafs[this.props.Versos[value].parentID].order; + const label = `Leaf ${leafOrder}: Side Verso}`; + sidesWithCurrentNote.push({label, value}) + } + return sidesWithCurrentNote; + } + + getRectosAndVersos = () => { + const size = Object.keys(this.props.Rectos).length; + let result = {}; + for (let i=0; i, + ]; + return ( + + + + ); + } + +} + + + + diff --git a/viscoll-app/src/components/collationManager/tabularMode/Group.js b/viscoll-app/src/components/collationManager/tabularMode/Group.js index dd6704b0..87d3b3b5 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Group.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Group.js @@ -1,115 +1,144 @@ import React from 'react'; import PropTypes from 'prop-types'; import Leaf from './Leaf'; -import {Card, CardText, CardHeader} from 'material-ui/Card'; -import tabularStyle from '../../../styles/tabular'; +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'; -/** Stateless functional component that displays one group in the tabular edit mode. Recursively mounts nested groups and leaves. */ -const Group = (props) => { - const { activeGroup } = props; - const { Leafs, Groups, Rectos, Versos } = props.project - const { - selectedObjects, - filters, - defaultAttributes, - visibleAttributes, - flashItems - } = props.collationManager; - const isActive = selectedObjects.members.includes(activeGroup.id); - const isFiltered = filters.Groups.includes(activeGroup.id); - const groupsOfMatchingElements = filters.GroupsOfMatchingLeafs + filters.GroupsOfMatchingSides + filters.GroupsOfMatchingNotes; - const isAffectedFiltered = groupsOfMatchingElements.includes(activeGroup.id) && !isFiltered; - const hideOthers = filters.hideOthers; - const isFilterActive = filters.active; - // Populate all the members of this Group. - let groupMembers = []; - activeGroup.memberIDs.forEach((memberID, index) => { - if (memberID.charAt(0)==="L"){ - let current_leaf = Leafs[memberID]; - groupMembers.push( - - ); - } else { - let current_group = Groups[memberID]; - groupMembers.push( - - ); - } - }); +/** Displays one group 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}); + } + + 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 = []; - for (var i in defaultAttributes.group) { - let attributeName = defaultAttributes.group[i].name; - if (visibleAttributes.group[attributeName]) { - attributes.push( -
-
- {defaultAttributes.group[i].displayName} - {activeGroup[attributeName]} + let attributes = []; + for (var i in this.props.collationManager.defaultAttributes.group) { + let attributeName = this.props.collationManager.defaultAttributes.group[i].name; + if (this.props.collationManager.visibleAttributes.group[attributeName]) { + attributes.push( +
+
+ {this.props.collationManager.defaultAttributes.group[i].displayName} + {this.props.activeGroup[attributeName]} +
-
- ); + ); + } } - } - let activeGroupStyle = {borderColor:"white"}; - if (isActive) { - activeGroupStyle["backgroundColor"] = "#4ED6CB"; - activeGroupStyle["borderColor"] = "#4ED6CB"; - } - if (isFiltered && !hideOthers) { - activeGroupStyle["borderColor"] = "#0f7fdb"; - } - if (isAffectedFiltered && hideOthers && isFilterActive){ - activeGroupStyle["backgroundColor"] = "#d9dbdb"; - activeGroupStyle["borderColor"] = "#d9dbdb"; - } - let groupComponent = - props.handleObjectClick(activeGroup, event)} - style={tabularStyle.group.cardHeader} + 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.order)) 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 {activeGroup.order} +
+
+
+ Group {this.props.activeGroup.order} + {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.activeGroup.order : "Expand group " + this.props.activeGroup.order } + tabIndex={this.props.tabIndex} + tooltip={this.state.open?"Collapse group" : "Expand group"} + > + {this.state.open? : } + +
- {attributes} +
+ {groupMembers}
- - - - {groupMembers} - - +
- if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) - groupComponent = ; + if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) + groupComponent =
; - return ( - groupComponent - ); -} + return ( + groupComponent + ); + } + } Group.propTypes = { /** Group object */ activeGroup: PropTypes.object, @@ -117,4 +146,5 @@ Group.propTypes = { handleObjectClick: PropTypes.func, } -export default Group; + + \ 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 index b2398ce4..b90a924c 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Leaf.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Leaf.js @@ -1,7 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {Card} from 'material-ui/Card'; -import tabularStyle from '../../../styles/tabular'; import Side from './Side'; /** Stateless functional component that displays one leaf in the tabular edit mode. */ @@ -52,11 +50,7 @@ const Leaf = (props) => { - let activeLeafStyle = {borderColor: "white"}; - if (isActive) { - activeLeafStyle["backgroundColor"] = "#4ED6CB"; - activeLeafStyle["borderColor"] = "#4ED6CB"; - } + let activeLeafStyle = {}; if (isFiltered && !hideOthers) { activeLeafStyle["borderColor"] = "#0f7fdb"; } @@ -79,29 +73,50 @@ const Leaf = (props) => { activeSide={rectoSide} collationManager={props.collationManager} handleObjectClick={props.handleObjectClick} + toggleFocusLeaf={props.toggleFocusLeaf} + focusLeafID={props.focusLeafID} + handleObjectPress={props.handleObjectPress} + tabIndex={props.tabIndex} />
); - let leafComponent =
props.handleObjectClick(activeLeaf, event)} + className={sectionStyle} + onClick={(event) => {props.handleObjectClick(activeLeaf, event);event.stopPropagation()}} style={{ ...activeLeafStyle }} + onMouseEnter={()=>props.toggleFocusLeaf(activeLeaf.id)} + onMouseLeave={()=>props.toggleFocusLeaf(null)} >
- Leaf {activeLeaf.order} + Leaf {activeLeaf.order} + {if(e.key===" "){props.handleObjectPress(activeLeaf, e)}}} + onClick={(e)=>{props.handleObjectPress(activeLeaf, e);e.stopPropagation();}} + tabIndex={props.tabIndex} + />
{leafAttributes.length>0?
@@ -111,10 +126,10 @@ const Leaf = (props) => {
{sideComponents}
- +
if (!isFiltered && hideOthers && isFilterActive && !isAffectedFiltered) - leafComponent = ; + leafComponent =
; return ( leafComponent diff --git a/viscoll-app/src/components/collationManager/tabularMode/Side.js b/viscoll-app/src/components/collationManager/tabularMode/Side.js index a29d75b1..a8bdf759 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Side.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Side.js @@ -34,11 +34,6 @@ const Side = (props) => { let activeSideStyle = {}; - if (isActive) { - activeSideStyle["backgroundColor"] = "#4ED6CB"; - activeSideStyle["borderColor"] = "#4ED6CB"; - } - if (isFiltered && !hideOthers) { activeSideStyle["borderColor"] = "#0f7fdb"; } @@ -49,25 +44,52 @@ const Side = (props) => { } 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)} + onClick={(event) => {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)} + className={classNames} + onClick={(event) => {props.handleObjectClick(activeSide, event); event.stopPropagation();}} style={{ ...activeSideStyle }} + onMouseEnter={()=>props.toggleFocusLeaf(activeSide.id)} + onMouseLeave={()=>props.toggleFocusLeaf(null)} > -
{activeSideName}
+
+ {activeSideName} + {if(e.key===" "){props.handleObjectPress(activeSide, e)}}} + onClick={(e)=>{props.handleObjectPress(activeSide, e);e.stopPropagation();}} + tabIndex={props.tabIndex} + /> +
{sideAttributes}
); diff --git a/viscoll-app/src/components/dashboard/CloneProject.js b/viscoll-app/src/components/dashboard/CloneProject.js index 07ce2f0b..eb8678ab 100644 --- a/viscoll-app/src/components/dashboard/CloneProject.js +++ b/viscoll-app/src/components/dashboard/CloneProject.js @@ -1,4 +1,5 @@ import React from 'react'; +import {floatFieldLight} from '../../styles/textfield'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; import SelectField from 'material-ui/SelectField'; @@ -19,48 +20,68 @@ export default class CloneProject extends React.Component { } submit = (event) => { - event.preventDefault(); + if (event) event.preventDefault(); this.props.cloneProject(this.props.allProjects[this.state.projectIndex].id); this.props.reset(); this.props.close(); } render(){ - return ( -
-

Clone Existing Collation

-
- - {this.props.allProjects.map((project, index)=>{ - return ( - 0) { + return ( +
+

Clone Existing Collation

+ + + {this.props.allProjects.map((project, index)=>{ + return ( + + ); + })} + +
+ this.props.previousStep()} + /> + this.submit(null)} /> - ); - })} - -
- + +
+ ); + } else { + return
+

Clone Existing Collation

+

You do not have any projects to clone.

+
+ - -
- + aria-label="Back" + onClick={() => this.props.previousStep()} + /> +
- ); + } } } diff --git a/viscoll-app/src/components/dashboard/EditProjectForm.js b/viscoll-app/src/components/dashboard/EditProjectForm.js index 2d2b681b..8f268aaf 100644 --- a/viscoll-app/src/components/dashboard/EditProjectForm.js +++ b/viscoll-app/src/components/dashboard/EditProjectForm.js @@ -24,6 +24,11 @@ class EditProjectForm extends React.Component { shelfmark: false, date: false, }, + errors: { + title: "", + shelfmark: "", + date: "", + }, }; } @@ -74,7 +79,7 @@ class EditProjectForm extends React.Component { * @public */ checkValidationError = (type) => { - const errors = {}; + const errors = {title:"", shelfmark:"", date:""}; const allProjectsExceptCurrent = [...this.state.allProjects]; allProjectsExceptCurrent.splice(this.state.selectedProjectIndex, 1); allProjectsExceptCurrent.forEach(project => { @@ -143,7 +148,7 @@ class EditProjectForm extends React.Component { * @public */ handleProjectUpdate = (event, field) => { - event.preventDefault(); + if (event) event.preventDefault(); const projectID = this.props.selectedProject.id; const project = { title: this.state.title, @@ -221,18 +226,21 @@ class EditProjectForm extends React.Component { return (
} style={{minWidth:"60px",marginLeft:"5px"}} name="submit" type="submit" disabled={this.ifErrorsExist()} + onClick={() => this.handleProjectUpdate(null, field)} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.handleProjectCancelUpdate(field)} + onClick={() => this.handleProjectCancelUpdate(field)} />
) @@ -246,7 +254,6 @@ class EditProjectForm extends React.Component { if (!selectedProject) return
; - let projectPanelData = (
this.handleProjectUpdate(e, "title")}> @@ -255,9 +262,12 @@ class EditProjectForm extends React.Component { floatingLabelFixed value={this.state.title} errorText={this.state.errors.title} + aria-invalid={this.state.errors.title.length>0} onChange={(event, newValue) => this.onInputChange(event, newValue, "title")} - floatingLabelStyle={{fontSize: 25}} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} fullWidth={true} + tabIndex={this.props.tabIndex} + autoFocus={true} /> {this.submitButtons("title")}
@@ -268,8 +278,9 @@ class EditProjectForm extends React.Component { value={this.state.shelfmark} errorText={this.state.errors.shelfmark} onChange={(event, newValue) => this.onInputChange(event, newValue, "shelfmark")} - floatingLabelStyle={{fontSize: 25}} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} fullWidth={true} + tabIndex={this.props.tabIndex} /> {this.submitButtons("shelfmark")} @@ -279,10 +290,12 @@ class EditProjectForm extends React.Component { floatingLabelFixed value={this.state.date} errorText={this.state.errors.date} + aria-invalid={this.state.errors.date.length>0} onChange={(event, newValue) => this.onInputChange(event, newValue, "date")} - floatingLabelStyle={{fontSize: 25}} + floatingLabelStyle={{fontSize: 25, color: "#526C91"}} fullWidth={true} hintText="N/A" + tabIndex={this.props.tabIndex} /> {this.submitButtons("date")} @@ -298,13 +311,13 @@ class EditProjectForm extends React.Component { this.handleDeleteDialogToggle()} + onClick={() => this.handleDeleteDialogToggle()} />, this.handleProjectDelete()} />, ]; @@ -312,22 +325,28 @@ class EditProjectForm extends React.Component { this.handleUnsavedDialogToggle()} + onClick={() => this.handleUnsavedDialogToggle()} />, this.handleProjectPanelClose(true)} + onClick={() => this.handleProjectPanelClose(true)} />, ]; return ( -
+
- - this.handleProjectPanelClose()} /> + this.handleProjectPanelClose()} + tabIndex={this.props.tabIndex} + > + {projectPanelData} @@ -335,16 +354,18 @@ class EditProjectForm extends React.Component {
this.props.history.push(`/project/${this.props.selectedProject.id}`)} - secondary + onClick={() => this.props.history.push(`/project/${this.props.selectedProject.id}`)} style={{width:"49%",float:"left",marginRight:"2%"}} + tabIndex={this.props.tabIndex} /> this.handleDeleteDialogToggle(true)} - labelColor="#D87979" + onClick={() => this.handleDeleteDialogToggle(true)} + labelColor="#b53c3c" style={{width:"49%"}} + tabIndex={this.props.tabIndex} /> { - event.preventDefault(); + if (event) event.preventDefault(); if (!this.isDisabled()) { this.props.importProject({importData: this.state.importData, importFormat: this.state.importFormat}); } @@ -44,7 +42,7 @@ export default class ImportProject extends React.Component { checkIfFileTypeIsInvalid = (file) => { - const allowedFileTypes = ["json", "xml", "txt"]; + const allowedFileTypes = ["json", "xml"]; return !allowedFileTypes.includes(file.type) } @@ -58,68 +56,75 @@ export default class ImportProject extends React.Component { } render() { - const dropFileText =

- Drop a file here, or click to select a file to upload.
- Only *.json, .xml and *.txt files will be accepted. -

; return (

Import

-

In the textbox below, please paste the content of your exported collation data.

+

Please paste the content of your exported collation data in the textbox below, or upload a file to import.

- this.onChange(v, "importData")} - underlineShow={false} - style={{border: "1px solid #cccccc", width: "99%"}} - textareaStyle={{padding:"0px 15px"}} + onChange={(e)=>this.onChange(e.target.value, "importData")} />
- {this.handleFileSelected(accepted)}} - accept=".json, .xml, .txt" - multiple={false} - > - {dropFileText} - -

Import format:

- this.onChange(v, "importFormat")} - > - Import from file: +
+ this.handleFileSelected(event.target.files)} + onClick={(event)=>event.target.value=null} /> - - - +
+

Import format:

+
+ this.onChange(v, "importFormat")} + > + + + +

- {this.props.importStatus} + {this.props.importStatus!==undefined? +

{this.props.importStatus}

+ : "" + }
- + this.props.previousStep()} + /> - // - // diff --git a/viscoll-app/src/components/dashboard/ListView.js b/viscoll-app/src/components/dashboard/ListView.js index 30375069..59cf3572 100644 --- a/viscoll-app/src/components/dashboard/ListView.js +++ b/viscoll-app/src/components/dashboard/ListView.js @@ -1,49 +1,39 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; /** * List the projects in a table format */ -const ListView = ({singleClickIndex, selectProject, allProjects=[], doubleClick}) => { - +const ListView = ({selectedProjectIndex, selectProject, allProjects=[], doubleClick, tabIndex}) => { + const selectedProjectID = selectedProjectIndex>=0? allProjects[selectedProjectIndex].id : null; + const viewDate = {year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'} + const ariaDate = {year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit'} const projectsList = allProjects.map((project, i) => { - var selected = singleClickIndex === i; return ( - selectProject(i)} onDoubleClick={()=>doubleClick(project.id)} - selected={selected} - style={{background:"rgba(255,255,255,0.2)", cursor:"pointer"}} + className={selectedProjectID===project.id? "selected":""} + tabIndex={tabIndex} > - {project.title} - {new Date(project.updated_at).toLocaleString('en-US')} - +
{project.title}
+
{new Date(project.updated_at).toLocaleString('en-US',viewDate)}
+ ); }); return ( - {selectProject(index[0])}} > - - - Name - Date Modified - - - +
+
Name
+
Date Modified
+
+
{projectsList} - -
+
+
); }; ListView.propTypes = { diff --git a/viscoll-app/src/components/dashboard/NewProjectChoice.js b/viscoll-app/src/components/dashboard/NewProjectChoice.js new file mode 100644 index 00000000..70c80f96 --- /dev/null +++ b/viscoll-app/src/components/dashboard/NewProjectChoice.js @@ -0,0 +1,26 @@ +import React from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; +import {btnMd} from '../../styles/button'; + +const NewProjectChoice = (props) => { + return
+ + + OR + + +
+} +export default NewProjectChoice; \ No newline at end of file diff --git a/viscoll-app/src/components/dashboard/NewProjectContainer.js b/viscoll-app/src/components/dashboard/NewProjectContainer.js index a46aa4d4..54627543 100644 --- a/viscoll-app/src/components/dashboard/NewProjectContainer.js +++ b/viscoll-app/src/components/dashboard/NewProjectContainer.js @@ -5,6 +5,7 @@ import ProjectDetails from './ProjectDetails'; import ProjectStructure from './ProjectStructure'; import ImportProject from './ImportProject'; import CloneProject from './CloneProject'; +import NewProjectChoice from './NewProjectChoice'; export default class NewProjectContainer extends React.Component { constructor(props) { @@ -15,7 +16,7 @@ export default class NewProjectContainer extends React.Component { title: "", shelfmark: "", date: "", - quireNo: 1, + quireNo: 2, leafNo: 10, conjoined: true, collationGroups: [], @@ -227,10 +228,21 @@ export default class NewProjectContainer extends React.Component { previousStep={this.reset} doErrorsExist={this.doErrorsExist} />; + } else if (this.state.step===2) { + content = this.set("step", 1)} + nextStep={()=>this.set("step",3)} + finish={this.finish} + /> } else { content = this.set("step", 1)} + previousStep={()=>this.setState({ + step: 1, + quireNo: 2, + leafNo: 10, + conjoined: true, + collationGroups: []})} set={this.set} quireNo={this.state.quireNo} leafNo={this.state.leafNo} @@ -264,18 +276,23 @@ export default class NewProjectContainer extends React.Component { /> ); } - return ( -
- this.handleRequestClose()} - className="newProjectDialog" - autoScrollBodyContent - > - {content} - -
- ); + if (this.props.open) { + return ( +
+ this.handleRequestClose()} + className="newProjectDialog" + autoScrollBodyContent + > + {content} + +
+ ); + } else { + return
; + } + } } diff --git a/viscoll-app/src/components/dashboard/NewProjectSelection.js b/viscoll-app/src/components/dashboard/NewProjectSelection.js index 9c47f1fb..a727ff2c 100644 --- a/viscoll-app/src/components/dashboard/NewProjectSelection.js +++ b/viscoll-app/src/components/dashboard/NewProjectSelection.js @@ -1,73 +1,70 @@ import React from 'react'; -import {Card, CardText} from 'material-ui/Card'; -import IconButton from 'material-ui/IconButton'; import AddIcon from 'material-ui/svg-icons/content/add'; import CopyIcon from 'material-ui/svg-icons/content/content-copy'; import ImportIcon from 'material-ui/svg-icons/action/system-update-alt'; const NewProjectSelection = (props) => { return ( -
- props.setProjectType("new")} +
+ -
- props.setProjectType("clone")} + + +
); } diff --git a/viscoll-app/src/components/dashboard/ProjectDetails.js b/viscoll-app/src/components/dashboard/ProjectDetails.js index 0960a1c3..a8a1bc91 100644 --- a/viscoll-app/src/components/dashboard/ProjectDetails.js +++ b/viscoll-app/src/components/dashboard/ProjectDetails.js @@ -1,13 +1,13 @@ import React from 'react'; +import {floatFieldLight} from '../../styles/textfield'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; - const ProjectDetails = (props) => { let submit = (event) => { - event.preventDefault(); + if (event) event.preventDefault(); if(!props.doErrorsExist()) props.nextStep() } return ( @@ -16,23 +16,32 @@ const ProjectDetails = (props) => {
0} onChange={(event, newValue) => props.set("title", newValue)} fullWidth /> 0} onChange={(event, newValue) => props.set("shelfmark", newValue)} fullWidth /> 0} onChange={(event, newValue) => props.set("date", newValue)} fullWidth /> @@ -40,13 +49,15 @@ const ProjectDetails = (props) => {
props.previousStep()} + aria-label="Back" />
diff --git a/viscoll-app/src/components/dashboard/ProjectStructure.js b/viscoll-app/src/components/dashboard/ProjectStructure.js index db1d4d5f..014e5dad 100644 --- a/viscoll-app/src/components/dashboard/ProjectStructure.js +++ b/viscoll-app/src/components/dashboard/ProjectStructure.js @@ -28,8 +28,6 @@ const ProjectStructure = (props) => { if (isDisabled) { return () @@ -37,8 +35,6 @@ const ProjectStructure = (props) => { return unconjoinLeafsList.map((val) => ( @@ -50,9 +46,10 @@ const ProjectStructure = (props) => { const unconjoinLeafsList = !group.leaves? [] : Array.from(Array(group.leaves).keys()); collationGroupsRows.push( - {group.number} - + {group.number} + { style={{width:50}} /> - - props.handleToggleConjoin(group)} - checked={group.conjoin} - disabled={group.leaves<=1} - style={{marginLeft:8}} - /> + + props.handleToggleConjoin(group)} + checked={group.conjoin} + disabled={group.leaves<=1} + style={{marginLeft:8}} + /> - - {props.onInputChangeCollationGroupsRows(e, group, "oddLeaf", value)}} - disabled={(!group.conjoin || group.leaves%2 === 0)} - > - {menuItems(group.oddLeaf, unconjoinLeafsList, (!group.conjoin || group.leaves%2 === 0))} - + + {props.onInputChangeCollationGroupsRows(e, group, "oddLeaf", value)}} + disabled={(!group.conjoin || group.leaves%2 === 0)} + fullWidth + > + {menuItems(group.oddLeaf, unconjoinLeafsList, (!group.conjoin || group.leaves%2 === 0))} + - + props.handleRemoveCollationGroupRow(group.number)} + aria-label="Remove" + onClick={() => props.handleRemoveCollationGroupRow(group.number)} > @@ -94,12 +96,17 @@ const ProjectStructure = (props) => { return (

Structure

-
-
+

+ Pre-populate your collation with quires and leaves by using the formula below. + Generate the items by clicking the "Add" button. You can add multiple times. +

+
+
+
{props.set("quireNo", parseInt(newValue, 10))}} @@ -107,12 +114,13 @@ const ProjectStructure = (props) => { type="number" />
-
+
+
{props.set("leafNo", parseInt(newValue, 10))}} @@ -124,6 +132,7 @@ const ProjectStructure = (props) => {
props.set("conjoined", !props.conjoined)} /> @@ -131,43 +140,44 @@ const ProjectStructure = (props) => {
props.addCollationRows()} primary + keyboardFocused />
{collationGroupsRows.length>0? -
+
- Quire no. - Number of leaves - Conjoin - Unconjoined leaf + Quire no. + Number of leaves + Conjoin + Unconjoined leaf {collationGroupsRows}
-
: -
- You can pre-populate your collation with quires and leaves by using the formula above. - Generate the groups and leaves by clicking the "Add" button. You can add multiple times. -
+
: "" }
props.previousStep()} /> + {props.collationGroups.length>0? + onClick={() => props.finish()} + />:""}
); diff --git a/viscoll-app/src/components/export/Export.js b/viscoll-app/src/components/export/Export.js index f4fb6978..098a253a 100644 --- a/viscoll-app/src/components/export/Export.js +++ b/viscoll-app/src/components/export/Export.js @@ -28,6 +28,7 @@ const Export = (props) => { label="Close" primary={true} onClick={()=>props.handleExportToggle(false)} + keyboardFocused />, ]; diff --git a/viscoll-app/src/components/filter/FilterRow.js b/viscoll-app/src/components/filter/FilterRow.js index c7823679..d81254b0 100644 --- a/viscoll-app/src/components/filter/FilterRow.js +++ b/viscoll-app/src/components/filter/FilterRow.js @@ -1,4 +1,5 @@ import React, {Component} from 'react'; +import {floatFieldLight} from '../../styles/textfield'; import SelectField from 'material-ui/SelectField'; import MenuItem from 'material-ui/MenuItem'; import TextField from 'material-ui/TextField'; @@ -42,10 +43,12 @@ class FilterRow extends Component { renderValueField = () => { let input =; if (this.props.attributeIndex!=="") { try { @@ -58,6 +61,8 @@ class FilterRow extends Component { multiple errorText={(this.props.type!==null && this.props.values.length===0)?"Required":""} style={{width:'100%'}} + tabIndex={this.props.tabIndex} + {...floatFieldLight} > {this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['options'].map(this.renderValueItems)} ); @@ -77,6 +82,7 @@ class FilterRow extends Component { listStyle={{ maxHeight: 300, overflow: 'auto' }} openOnFocus={true} style={{width:'100%'}} + tabIndex={this.props.tabIndex} />); } else if (this.props.defaultAttributes[this.props.type] && this.props.defaultAttributes[this.props.type][this.props.attributeIndex]['name']==="type"){ @@ -97,6 +103,7 @@ class FilterRow extends Component { listStyle={{ maxHeight: 300, overflow: 'auto' }} openOnFocus={true} style={{width:'100%'}} + tabIndex={this.props.tabIndex} />); } else { @@ -105,6 +112,8 @@ class FilterRow extends Component { hintText="Value" style={{paddingTop: 24, width: '100%'}} onChange={(e,v)=>this.props.onChange(this.props.queryIndex,"values",e,0,[v])} + tabIndex={this.props.tabIndex} + {...floatFieldLight} />; } } catch (e) {} @@ -122,6 +131,8 @@ class FilterRow extends Component { onChange={(e,i,v)=>{let queryIndex = this.props.queryIndex; this.props.onChange(queryIndex,"type",e,i,v);}} style={{width:'100%'}} disabled={this.props.disableNewRow} + tabIndex={this.props.tabIndex} + {...floatFieldLight} > @@ -139,6 +150,8 @@ class FilterRow extends Component { errorText={(this.props.type!==null && this.props.attribute==="")?"Required":""} autoWidth disabled={this.props.disableNewRow} + tabIndex={this.props.tabIndex} + {...floatFieldLight} > {this.renderAttributeMenuItems()} @@ -152,6 +165,8 @@ class FilterRow extends Component { style={{width:'100%'}} errorText={(this.props.type!==null && this.props.condition==="")?"Required":""} disabled={this.props.disableNewRow} + tabIndex={this.props.tabIndex} + {...floatFieldLight} > {['equals', 'contains', 'not equals', 'not contains'].filter((item)=>this.filterConditionItems(item)).map(this.mapConditionItems)} @@ -169,6 +184,8 @@ class FilterRow extends Component { style={{width:'100%'}} disabled={this.props.lastRow} errorText={(!this.props.lastRow && this.props.conjunction==="")?"Required":""} + tabIndex={this.props.tabIndex} + {...floatFieldLight} > @@ -177,17 +194,20 @@ class FilterRow extends Component {
this.props.removeRow(this.props.queryIndex)} - style={(this.props.queryIndex===0 && this.props.queriesLength===1)? {opacity:0,pointerEvents:'none'}: {}} + aria-label="Remove filter query row" + onClick={()=>this.props.removeRow(this.props.queryIndex)} + style={(this.props.queryIndex===0 && this.props.queriesLength===1)? {display:"none"}: {}} > this.props.addRow()} + onClick={()=>this.props.addRow()} style={(this.props.queryIndex===this.props.queriesLength-1)? {marginLeft:10} : {opacity:0,pointerEvents:'none',marginLeft:10}} secondary disabled={this.props.disableAddRow} + tabIndex={this.props.tabIndex} > @@ -196,9 +216,8 @@ class FilterRow extends Component { return row; } - render() { - return this.renderRow(); + return this.renderRow(); } } diff --git a/viscoll-app/src/components/global/PageNotFound.js b/viscoll-app/src/components/global/PageNotFound.js index 611a967e..337cf8c4 100644 --- a/viscoll-app/src/components/global/PageNotFound.js +++ b/viscoll-app/src/components/global/PageNotFound.js @@ -11,7 +11,7 @@ export default class PageNotFound extends Component { this.props.history.push("/dashboard")} + onClick={()=>this.props.history.push("/dashboard")} />
diff --git a/viscoll-app/src/components/global/Panel.js b/viscoll-app/src/components/global/Panel.js index 8f302f1b..6de1e475 100644 --- a/viscoll-app/src/components/global/Panel.js +++ b/viscoll-app/src/components/global/Panel.js @@ -1,32 +1,43 @@ import React, {Component} from 'react'; -import sidebarStyle from "../../styles/sidebar"; -import {Card, CardText, CardHeader} from 'material-ui/Card'; +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'; /** Expandable panel component for the project sidebar. Panel examples: Filter, export.. */ export default class Panel extends Component { + constructor(props) { + super(props); + this.state = { + open: props.defaultOpen, + } + } + + handleChange = (type, value) => { + this.setState({[type]: value}); + } + render() { + let paddingStyle = this.props.noPadding?{padding:0}:{}; return ( - - - +
+
+

{this.props.title}

+ {e.preventDefault(); e.stopPropagation(); this.handleChange("open", !this.state.open)}} + aria-label={this.state.open?"Hide " + this.props.title + " panel" : "Expand " + this.props.title + " panel"} + iconStyle={{color:"white"}} + tooltip={this.state.open?"Hide panel":"Expand panel"} + style={{padding:0,width:"inherit",height:"inherit"}} + tooltipPosition="bottom-left" + tabIndex={this.props.tabIndex} + > + {this.state.open? : } + +
+
{this.props.children} - - +
+
) } diff --git a/viscoll-app/src/components/imageManager/AddManifest.js b/viscoll-app/src/components/imageManager/AddManifest.js index 03ef322d..d6b648d2 100644 --- a/viscoll-app/src/components/imageManager/AddManifest.js +++ b/viscoll-app/src/components/imageManager/AddManifest.js @@ -60,20 +60,22 @@ export default class AddManifest extends Component {
this.onChange("url", v)} + tabIndex={this.props.tabIndex} />
-
this.onCancel(e)} style={{marginRight: 5}} + tabIndex={this.props.tabIndex} /> this.onSubmit(e)} + tabIndex={this.props.tabIndex} />
diff --git a/viscoll-app/src/components/imageManager/DeleteManifest.js b/viscoll-app/src/components/imageManager/DeleteManifest.js index 32e65576..a4bcf23d 100644 --- a/viscoll-app/src/components/imageManager/DeleteManifest.js +++ b/viscoll-app/src/components/imageManager/DeleteManifest.js @@ -10,11 +10,12 @@ export default class DeleteManifest extends Component { label="Cancel" primary={true} onClick={this.props.handleClose} + keyboardFocused />, {this.props.handleClose(); this.props.deleteManifest()}} - backgroundColor="#D87979" + backgroundColor="#b53c3c" labelColor="#ffffff" />, ]; diff --git a/viscoll-app/src/components/imageManager/EditManifest.js b/viscoll-app/src/components/imageManager/EditManifest.js index 102b4c31..b5a59cdd 100644 --- a/viscoll-app/src/components/imageManager/EditManifest.js +++ b/viscoll-app/src/components/imageManager/EditManifest.js @@ -82,14 +82,16 @@ export default class EditManifest extends Component {
this.onSubmit(e)}>
-
Manifest name
+
Manifest name
this.onChange("name", v)} fullWidth + autoFocus />
diff --git a/viscoll-app/src/components/imageManager/ManageManifests.js b/viscoll-app/src/components/imageManager/ManageManifests.js index ebf47a98..705c5465 100644 --- a/viscoll-app/src/components/imageManager/ManageManifests.js +++ b/viscoll-app/src/components/imageManager/ManageManifests.js @@ -7,7 +7,7 @@ import DeleteManifest from './DeleteManifest'; import ImageViewer from "../global/ImageViewer"; import Dialog from 'material-ui/Dialog'; -class ManageManifests extends Component { +export default class ManageManifests extends Component { constructor(props) { super(props); this.state = { @@ -20,14 +20,17 @@ class ManageManifests extends Component { } handleOpen = (name, openManifest) => { this.setState({[name]: true, openManifest}); + this.props.togglePopUp(true); }; handleClose = (name) => { this.setState({[name]: false}); + this.props.togglePopUp(false); }; toggleImageModal = (imageModalOpen, activeImage) => { - this.setState({imageModalOpen, activeImage}) + this.setState({imageModalOpen, activeImage}); + this.props.togglePopUp(imageModalOpen); } renderManifest = (manifestID) => { @@ -44,21 +47,33 @@ class ManageManifests extends Component {
{manifest.images.slice(0,4).map((img) => ( -
+
+ ))}
- this.handleOpen("editOpen", manifest)} /> - this.handleOpen("deleteOpen", manifest)} /> + this.handleOpen("editOpen", manifest)} + tabIndex={this.props.tabIndex} + /> + this.handleOpen("deleteOpen", manifest)} + tabIndex={this.props.tabIndex} + />
@@ -75,6 +90,7 @@ class ManageManifests extends Component { createManifest={this.props.createManifest} createManifestError={this.props.createManifestError} cancelCreateManifest={this.props.cancelCreateManifest} + tabIndex={this.props.tabIndex} /> 0? Object.keys(props.manifests)[0]:"", - initiallyLinkedSides: [], + imageMapBoard: [], // [{manifestID: "", label: "", url: ""}, ...] + sideMapBoard: [], // [sideID, ...] + imageBacklog: [], // [{manifestID: "", label: "", url: ""}, ...] + sideBacklog: [], // [sideID, ...] + activeManifest: this.props.manifests[Object.keys(this.props.manifests)[0]], // {id: "", url: "", images: []} + initialMapping: {imageMapBoard: [], sideMapBoard: [], imageBacklog: {}, sideBacklog: []}, + selectedObjects: {type: "", members: [], lastSelected: null}, imageModalOpen: false, - activeImage: null + activeImage: null // "url" } } - componentWillUnmount = () => { - cancelAnimationFrame(this.requestedFrame) - } - - scheduleUpdate = (updateFn) => { - this.pendingUpdateFn = updateFn - if (!this.requestedFrame) { - this.requestedFrame = requestAnimationFrame(this.drawFrame) - } - } - - toggleImageModal = (imageModalOpen, activeImage) => { - this.setState({imageModalOpen, activeImage}) - } - - drawFrame = () => { - const nextState = update(this.state, this.pendingUpdateFn) - this.setState(nextState) - this.pendingUpdateFn = null - this.requestedFrame = null - } - componentWillMount() { - let imageBacklogs = {}; - let sideBacklogByID = {}; - let sideBacklog = []; - let sideMapBoardByID = {}; - let sideMapBoard = []; - let imageMapBoard = []; - let imageMapBoardByID = {}; - let linkedImages = {}; - let initiallyLinkedSides = []; - - // Set up linkedImages dictionary - for (const manifestID in this.props.manifests) { - linkedImages[manifestID]=[]; + // Update initial map board with already existing mappings + const { Rectos, rectoIDs, Versos, versoIDs } = this.props; + let imageBacklog = []; + for (let manifest of Object.entries(this.props.manifests)) { + imageBacklog = imageBacklog.concat(manifest[1].images); } - - const rectoIDs = Object.keys(this.props.Rectos); - const versoIDs = Object.keys(this.props.Versos); - for (const i in rectoIDs) { - const recto = this.props.Rectos[rectoIDs[i]]; - const verso = this.props.Versos[versoIDs[i]]; - const rectoDraggableItem = {id: recto.id, sideType: "Recto", leafOrder: recto.parentOrder, folioNumber: recto.folio_number}; - const versoDraggableItem = {id: verso.id, sideType: "Verso", leafOrder: verso.parentOrder, folioNumber: verso.folio_number}; - // Add sides to board or backlog depending if they're linked to images - if (recto.image.manifestID!==undefined && recto.image.manifestID.length>0) { - sideMapBoardByID[recto.id]=(rectoDraggableItem); - sideMapBoard.push(rectoDraggableItem); - const imgObj = {id: recto.image.label, manifestID: recto.image.manifestID, url: recto.image.url, binOrigin:"imageBacklog_"+this.props.manifests[recto.image.manifestID].name}; - imageMapBoard.push(imgObj); - imageMapBoardByID[recto.image.label] = imgObj; - linkedImages[recto.image.manifestID].push(recto.image.url); - initiallyLinkedSides.push({id: recto.id, url: recto.image.url}); - } else { - sideBacklog.push(rectoDraggableItem); - sideBacklogByID[recto.id]=rectoDraggableItem; - } - if (verso.image.manifestID!==undefined && verso.image.manifestID.length>0) { - sideMapBoard.push(versoDraggableItem); - sideMapBoardByID[verso.id]=(versoDraggableItem); - const imgObj = {id: verso.image.label, manifestID: verso.image.manifestID, url: verso.image.url, binOrigin:"imageBacklog_"+this.props.manifests[verso.image.manifestID].name}; - imageMapBoard.push(imgObj) - imageMapBoardByID[verso.image.label] = imgObj; - linkedImages[verso.image.manifestID].push(verso.image.url); - initiallyLinkedSides.push({id: verso.id, url: verso.image.url}); - } else { - sideBacklog.push(versoDraggableItem); - sideBacklogByID[verso.id]=versoDraggableItem; - } - } - for (const manifestID in this.props.manifests) { - const manifest = this.props.manifests[manifestID]; - // Add the initial parent bin to each image object - // const images = manifest.images.filter((image)=>{return !linkedImages[manifestID].includes(image.url)}).map((image)=>{return {id: image.label, manifestID: manifestID, url: image.url, binOrigin:"imageBacklog_"+manifest.name}}); - const images = manifest.images.filter((image)=>{return !linkedImages[manifestID].includes(image.url)}); - let imageBacklog = []; - let imageBacklogByID = {}; - for (const image of images) { - const imgObj = {id: image.label, manifestID: manifestID, url: image.url, binOrigin:"imageBacklog_"+manifest.name}; - imageBacklog.push(imgObj) - imageBacklogByID[image.label] = imgObj; + let sideBacklog = [...rectoIDs].map((rectoID, index) => [rectoID, versoIDs[index]]).reduce((a,b)=> a.concat(b), []); + let imageMapBoard = []; + let sideMapBoard = []; + // Add sides to board or backlog depending if they're linked to images + for (const sideID of sideBacklog) { + const side = sideID.charAt(0)==="R" ? Rectos[sideID] : Versos[sideID]; + if (side.image.label){ + sideMapBoard.push(sideID); + imageMapBoard.push(side.image); } - imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')+"ByID"] = {...imageBacklogByID}; - imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')] = imageBacklog; } - // console.log(imageBacklogs); - this.setState({...imageBacklogs, imageMapBoard, imageMapBoardByID, sideMapBoard, sideMapBoardByID, sideBacklog, sideBacklogByID, initiallyLinkedSides}); + // Remove items from backlog which already exists in the intial map board + imageBacklog = imageBacklog.filter(backlogImage => !imageMapBoard.find(mapImage => mapImage.label===backlogImage.label && mapImage.manifestID===backlogImage.manifestID)); + sideBacklog = sideBacklog.filter(sideID => !sideMapBoard.includes(sideID)); + this.setState({imageBacklog, sideBacklog, sideMapBoard, imageMapBoard, initialMapping:{imageBacklog, sideBacklog, sideMapBoard, imageMapBoard}}); } - moveItem = (id, afterId, binName) => { - // console.log("moveItem", id, afterId, binName); - if (binName.includes("Backlog")) binName = binName.substring(0,38).replace(/ /g, ''); - - const binByID = this.state[binName+"ByID"]; - const binByIndex = this.state[binName]; + componentWillReceiveProps(nextProps) { + let members = []; + if (nextProps.selectAll!=="") + members = [...this.state[nextProps.selectAll]]; + const selectedObjects = {type: nextProps.selectAll, members, lastSelected: members[-1]}; + this.setState({ selectedObjects }); + } - const item = binByID[id] - const afterItem = binByID[afterId] + handleManifestChange = activeManifestID => this.setState({activeManifest: this.props.manifests[activeManifestID]}); - const itemIndex = binByIndex.indexOf(item); - const afterIndex = binByIndex.indexOf(afterItem); + toggleImageModal = (imageModalOpen, activeImage) => this.setState({imageModalOpen, activeImage}); - this.scheduleUpdate({ - [binName]: { - $splice: [[itemIndex, 1], [afterIndex, 0, item]], - }, - }) + resetChanges = () => { + const { imageBacklog, sideBacklog, sideMapBoard, imageMapBoard } = this.state.initialMapping; + const selectedObjects = {type: "", members: [], lastSelected: null}; + const activeManifest = this.props.manifests[Object.keys(this.props.manifests)[0]]; + this.setState({ imageBacklog, sideBacklog, sideMapBoard, imageMapBoard, selectedObjects, activeManifest }); } - - changeBins = (fromBinID, toBinID, item, addToFrontOfList) => { - // console.log("changeBins", fromBinID, toBinID, item, addToFrontOfList); - if (fromBinID.includes("Backlog")) { - fromBinID = fromBinID.substring(0,38).replace(/ /g, ''); - } else if (toBinID.includes("Backlog")) { - toBinID = toBinID.substring(0,38).replace(/ /g, ''); + handleObjectClick = (type, object, event) => { + let selectedObjects = {...this.state.selectedObjects, members: [...this.state.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 !== type) { + selectedObjects.members = []; + selectedObjects.type = type; + } + let index; + if (type.includes("image")) + index = selectedObjects.members.findIndex(member => member.label===object.label && member.manifestID===object.manifestID); + else + index = selectedObjects.members.indexOf(object); + (index!==-1) ? selectedObjects.members.splice(index, 1) : selectedObjects.members.push(object); } - let fromBin = this.state[fromBinID]; - fromBin.splice(fromBin.indexOf(item),1); - let fromBinByID = this.state[fromBinID+"ByID"]; - delete fromBinByID[item.id]; - let toBin = this.state[toBinID]; - addToFrontOfList ? toBin.unshift(item) : toBin.push(item); - let toBinByID = this.state[toBinID+"ByID"]; - toBinByID[item.id]=item; - // updating the state inside a requestAnimationFrame callback, - if (!this.requestedFrame) { - this.requestedFrame = requestAnimationFrame(()=>{ - this.setState({[fromBinID]:fromBin, [toBinID]:toBin, [fromBinID+"ByID"]:fromBinByID, [toBinID+"ByID"]:toBinByID}) - this.pendingUpdateFn = null - this.requestedFrame = null - }) + 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)) { + selectedObjects.members = []; + } + else { + selectedObjects.members = [object]; + } + } + if (event.shiftKey || (event.modifiers!==undefined && event.modifiers.shift)) { + window.getSelection().removeAllRanges(); + // Object type changed, clear all active selected objects + if (selectedObjects.type !== type) { + selectedObjects.members = [object]; + } else { + // Select all similar type objects within this object and last selected object + let allMembers = [...this.state[type]]; + let indexOfCurrentElement, indexOfLastElement; + if (type.includes("image")){ + indexOfCurrentElement = allMembers.findIndex(member => member.label===object.label && member.manifestID===object.manifestID); + indexOfLastElement = allMembers.findIndex(member => member.label===selectedObjects.lastSelected.label && member.manifestID===selectedObjects.lastSelected.manifestID); + } + else { + indexOfCurrentElement = allMembers.indexOf(object); + indexOfLastElement = allMembers.indexOf(selectedObjects.lastSelected); + } + let indexes = [indexOfLastElement, indexOfCurrentElement]; + indexes.sort((a, b) => {return a-b}); + const currentSelected = [...selectedObjects.members]; + selectedObjects.members = allMembers.slice(indexes[0], indexes[1]+1); + for (let object of currentSelected){ + if (!selectedObjects.members.includes(object)) + selectedObjects.members.push(object); + } + } + } + } + if (selectedObjects.members.length === 0) { + selectedObjects.type = ""; + } else { + selectedObjects.type = type; + selectedObjects.lastSelected = object; } + this.setState({ selectedObjects }); } - getIndex = (obj, parentBoardID) => { - if (parentBoardID.includes("Backlog")) parentBoardID = parentBoardID.substring(0,38).replace(/ /g, ''); - return this.state[parentBoardID].indexOf(obj); + moveItemUpOrDown = (item, mapBoardType, position) => { + let newMapBoard = [...this.state[mapBoardType]]; + let indexOfItem; + if (mapBoardType==="imageMapBoard"){ + indexOfItem = newMapBoard.findIndex(image => image.label===item.label && image.manifestID===item.manifestID); + } else { + indexOfItem = newMapBoard.indexOf(item); + } + const indexOfSwappingItem = position==="down" ? indexOfItem+1 : indexOfItem-1; + const swappedItem = newMapBoard[indexOfSwappingItem]; + newMapBoard[indexOfItem] = swappedItem; + newMapBoard[indexOfSwappingItem] = item; + this.setState({ [mapBoardType]: newMapBoard }); } - - handleChange = (event, index, activeManifest) => this.setState({activeManifest}); - addAll = (fromBoard, toBoard) => { - const newToList = this.state[toBoard].concat(this.state[fromBoard]); - this.setState({[fromBoard]:[], [toBoard]:newToList}); + moveItemsToMap = (items=this.state.selectedObjects.members, mapBoardType, backlogBoardType) => { + let newMapBoard = [...this.state[mapBoardType], ...items]; + let newBacklogBoard; + if (mapBoardType==="imageMapBoard"){ + newBacklogBoard = [...this.state[backlogBoardType]].filter(image => !items.find(item => item.label===image.label && item.manifestID===image.manifestID)); + } else { + newBacklogBoard = [...this.state[backlogBoardType]].filter(item => !items.includes(item)); + } + let selectedObjects = {...this.state.selectedObjects, members: [...this.state.selectedObjects.members]}; + selectedObjects.type = mapBoardType; + this.setState({ [mapBoardType]: newMapBoard, [backlogBoardType]: newBacklogBoard, selectedObjects }); } - resetImageBacklog = () => { - let imageBacklogs = {}; - for (const manifestID in this.props.manifests) { - const manifest = this.props.manifests[manifestID]; - // Add the initial parent bin to each image object - const images = manifest.images.map((image)=>{return {id: image.label, url: image.url, binOrigin:"imageBacklog_"+manifest.name}}); - imageBacklogs["imageBacklog_"+manifest.name.substring(0,25).replace(/ /g, '')] = images; + moveItemsToBacklog = (items=this.state.selectedObjects.members, mapBoardType, backlogBoardType) => { + let newBacklogBoard = [...this.state[backlogBoardType], ...items]; + let newMapBoard; + if (mapBoardType==="imageMapBoard"){ + newMapBoard = [...this.state[mapBoardType]].filter(image => !items.find(item => item.label===image.label && item.manifestID===image.manifestID)); + newBacklogBoard.sort((a, b)=>this.state.initialMapping[backlogBoardType].findIndex(image => image.label===a.label && image.manifestID===a.manifestID) > this.state.initialMapping[backlogBoardType].findIndex(image => image.label===b.label && image.manifestID===b.manifestID) ? 1 : -1); + // newBacklogBoard.sort((a, b)=>a.label>b.label ? 1 : -1); + } else { + newMapBoard = [...this.state[mapBoardType]].filter(item => !items.includes(item)); + // newBacklogBoard.sort((a, b)=> { + // const sideA = a.charAt(0)==="R" ? this.props.Rectos[a] : this.props.Versos[a]; + // const sideB = b.charAt(0)==="R" ? this.props.Rectos[b] : this.props.Versos[b]; + // return (sideA.parentOrder>sideB.parentOrder) ? 1 : -1 + // }); + newBacklogBoard.sort((a, b)=>this.state.initialMapping[backlogBoardType].indexOf(a) > this.state.initialMapping[backlogBoardType].indexOf(b) ? 1 : -1); } - this.setState({imageMapBoard:[], ...imageBacklogs}); + let selectedObjects = {...this.state.selectedObjects, members: [...this.state.selectedObjects.members]}; + selectedObjects.type = backlogBoardType; + this.setState({ [mapBoardType]: newMapBoard, [backlogBoardType]: newBacklogBoard, selectedObjects }); } + submitIsDisabled = () => { + // check for changes in sideMapBoard + const sideMapBoard = this.state.sideMapBoard; + const initialSideMapBoard = this.state.initialMapping.sideMapBoard; + let noChangesInSideMapBoard = sideMapBoard.length===initialSideMapBoard.length && sideMapBoard.every((v,i) => v===initialSideMapBoard[i]); + // check for changes in imageMapBoard + const imageMapBoard = this.state.imageMapBoard; + const initialImageMapBoard = this.state.initialMapping.imageMapBoard; + let noChangesInImageMapBoard = imageMapBoard.length===initialImageMapBoard.length && imageMapBoard.every((v,i) => v.label===initialImageMapBoard[i].label && v.manifestID===initialImageMapBoard[i].manifestID); + // compare both changes + const noChanges = noChangesInSideMapBoard && noChangesInImageMapBoard; const unevenMatches = this.state.sideMapBoard.length!==this.state.imageMapBoard.length; - const noNewItems = this.state.sideMapBoard.length===this.state.initiallyLinkedSides.length && (this.state.sideMapBoard.filter((side, index)=>this.state.initiallyLinkedSides.find((initSide)=>{return initSide.id===side.id && this.state.imageMapBoard[index]!==undefined && initSide.url===this.state.imageMapBoard[index].url})!==undefined)).length===this.state.sideMapBoard.length; - const wantToUnlinkEverything = this.state.initiallyLinkedSides.length>0 && this.state.sideMapBoard.length===0 && this.state.imageMapBoard.length===0; - return !wantToUnlinkEverything && (unevenMatches || noNewItems); + return unevenMatches || noChanges; } - submitMapping = () => { - if (!this.submitIsDisabled()) { - let unlinkedSideIDs = this.state.initiallyLinkedSides.filter((initialSide)=>{ - const stillInBoard = this.state.sideMapBoard.filter((side)=>{return side.id===initialSide.id}); - return stillInBoard.length===0; - }).map((item)=>item.id); - this.setState({initiallyLinkedSides: this.state.sideMapBoard.map((obj, index)=>{return {id: obj.id, url: this.state.imageMapBoard[index].url}})}, ()=>{this.props.mapSidesToImages(this.state.sideMapBoard, this.state.imageMapBoard, unlinkedSideIDs)}); - } + automatchIsDisabled = () => { + for (const sideID of this.state.sideBacklog) { + const side = sideID.charAt(0)==="R" ? this.props.Rectos[sideID] : this.props.Versos[sideID]; + // Return immediately if a match is found + if (this.state.imageBacklog.find(image => image.label.includes(side.folio_number))) return false; + } + return true; } automatch = () => { - let sidesToMap = []; - let imagesToMap = {}; - for (const side of this.state.sideBacklog) { - let imageMatch; - // Look through manifests to find an image with an id equal to the side's folio number - for (const manifestID in this.props.manifests) { - const manifestName = this.props.manifests[manifestID].name; - imageMatch = this.state["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].find((image)=>image.id===side.folioNumber); - if (imageMatch!==undefined) { - // Found a match! Record the side and image - sidesToMap.push(side); - if (!imagesToMap.hasOwnProperty("imageBacklog_"+manifestName.substring(0,25).replace(/ /g, ''))) imagesToMap["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')]=[]; - imagesToMap["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].push(imageMatch); - break; - } - } - } - // Add items to the board - this.moveMultipleItems("sideBacklog", "sideMapBoard", sidesToMap, false); - for (const imgBinName in imagesToMap) { - this.moveMultipleItems(imgBinName, "imageMapBoard", imagesToMap[imgBinName], false); + let sideItemsToMap = []; + let imageItemsToMap = []; + for (const sideID of this.state.sideBacklog) { + const side = sideID.charAt(0)==="R" ? this.props.Rectos[sideID] : this.props.Versos[sideID]; + const image = this.state.imageBacklog.find(image => image.label.endsWith(side.folio_number)) + if (image){ + sideItemsToMap.push(sideID); + imageItemsToMap.push(image); + } } + this.moveItemsToMap(sideItemsToMap, "sideMapBoard", "sideBacklog"); + this.moveItemsToMap(imageItemsToMap, "imageMapBoard", "imageBacklog"); } - - moveMultipleItems = (fromBinID, toBinID, items, addToFrontOfList) => { - let fromBin = this.state[fromBinID]; - let fromBinByID = this.state[fromBinID+"ByID"]; - let toBin = this.state[toBinID]; - let toBinByID = this.state[toBinID+"ByID"]; - for (const item of items) { - fromBin.splice(fromBin.indexOf(item),1); - delete fromBinByID[item.id]; - addToFrontOfList ? toBin.unshift(item) : toBin.push(item); - toBinByID[item.id]=item; - } - - // updating the state inside a requestAnimationFrame callback, - if (!this.requestedFrame) { - this.requestedFrame = requestAnimationFrame(()=>{ - this.setState({[fromBinID]:fromBin, [toBinID]:toBin, [fromBinID.substring(0,25).replace(/ /g, '')+"ByID"]:fromBinByID, [toBinID.substring(0,25).replace(/ /g, '')+"ByID"]:toBinByID}) - this.pendingUpdateFn = null - this.requestedFrame = null - }) + + submitMapping = () => { + let newSideMappings = []; + let unlikedSideMappings = []; + let sideIDsWithImage = this.state.sideMapBoard.map((sideID, i) => [sideID, this.state.imageMapBoard[i]]); + for (let [sideID, image] of sideIDsWithImage){ + newSideMappings.push({ id: sideID, attributes: {image} }); } + for (let sideID of this.state.initialMapping.sideMapBoard) { + if (this.state.sideMapBoard.indexOf(sideID)===-1) + unlikedSideMappings.push({ id: sideID, attributes: {image: {}}}) + } + this.props.mapSidesToImages(newSideMappings.concat(unlikedSideMappings)); } - automatchDisabled = () => { - for (const side of this.state.sideBacklog) { - // Look through manifests to find an image with an id equal to the side's folio number - for (const manifestID in this.props.manifests) { - const manifestName = this.props.manifests[manifestID].name; - // console.log(("imageBacklog_"+manifestName), this.state["imageBacklog_"+manifestName]); - const imageMatch = this.state["imageBacklog_"+manifestName.substring(0,25).replace(/ /g, '')].find((image)=>image.id===side.folioNumber); - if (imageMatch!==undefined) { - // Found a match! - return false; - } - } - } - return true; - } - + render() { - if (Object.keys(this.props.manifests).length>0) { + if (Object.keys(this.props.manifests).length<1) { return ( -
-
-
-
-
-
- this.addAll("sideMapBoard", "sideBacklog")} - > - - -
-
-
-
-
- - - -
-
-
-
-
- -
-
- -
-
+
+

Getting started

+

To start mapping images to the collation, please upload a manifest in the "Manage Sources" tab.

+
+ ); + } + + const mapBoard = ( + + ); + + const middlePanel = ( +
+
+ this.moveItemsToMap(undefined, "sideMapBoard", "sideBacklog")} + disabled={this.state.selectedObjects.members.length===0 || this.state.selectedObjects.type!=="sideBacklog"} + style={{marginRight:"0.2em"}} + tabIndex={this.props.tabIndex} + /> + this.moveItemsToBacklog(undefined, "sideMapBoard", "sideBacklog")} + disabled={this.state.selectedObjects.members.length===0 || this.state.selectedObjects.type!=="sideMapBoard"} + tabIndex={this.props.tabIndex} + /> +
+
+ +
+
+ this.moveItemsToMap(undefined, "imageMapBoard", "imageBacklog")} + disabled={this.state.selectedObjects.members.length===0 || this.state.selectedObjects.type!=="imageBacklog"} + style={{marginRight:"0.2em"}} + tabIndex={this.props.tabIndex} + /> + this.moveItemsToBacklog(undefined, "imageMapBoard", "imageBacklog")} + disabled={this.state.selectedObjects.members.length===0 || this.state.selectedObjects.type!=="imageMapBoard"} + tabIndex={this.props.tabIndex} + />
- -
-
-
-
Sides backlog
-
- this.addAll("sideBacklog", "sideMapBoard")} - > - - -
-
-
- -
-
-
-
-
Images backlog
-
- this.addAll("imageBacklog_"+this.props.manifests[this.state.activeManifest].name.substring(0,25).replace(/ /g, ''), "imageMapBoard")} - > - - -
-
-
-
Manifest:
-
- - {Object.keys(this.props.manifests).map((manifestID)=> - 40? this.props.manifests[manifestID].name.slice(0,40) + "..." : this.props.manifests[manifestID].name} /> - )} - -
-
-
- {Object.keys(this.props.manifests).map((manifestID)=> { - const manifest = this.props.manifests[manifestID]; - return + ); + + const sideBacklog = ( +
+
+
Sides backlog
+
+
+ +
+
+ ); + + const imageBacklog = ( +
+
+
Image Backlog
+
+
+ this.handleManifestChange(manifestID)} + underlineStyle={{border:0}} + labelStyle={{height: 40, lineHeight: "35px", top:0}} + iconStyle={{height: 40, padding:0}} + fullWidth + tabIndex={this.props.tabIndex} + > + {Object.entries(this.props.manifests).map(([manifestID, manifest])=> + - })} -
+ )} +
-
-
Mapping {this.state.sideMapBoard.length} sides to {this.state.imageMapBoard.length} images
-
- - -
+
+ +
+ backlogImage.manifestID===this.state.activeManifest.id)} + activeManifest={this.state.activeManifest} + manifests={this.props.manifests} + toggleImageModal={this.toggleImageModal} + handleObjectClick={this.handleObjectClick} + selectedObjects={this.state.selectedObjects} + moveItemsToMap={this.moveItemsToMap} + tabIndex={this.props.tabIndex} + /> +
+
+ ); + + const mainToolBar = ( +
+
Mapping {this.state.sideMapBoard.length} sides to {this.state.imageMapBoard.length} images
+
+ + +
+
+ ); + + const imageViewerModal = ( + this.toggleImageModal(false)} + contentStyle={{background: "none", boxShadow: "inherit"}} + bodyStyle={{padding:0}} + > + + + ); + + return ( +
+
+
+ {mapBoard}
- this.toggleImageModal(false)} - contentStyle={{background: "none", boxShadow: "inherit"}} - bodyStyle={{padding:0}} - > - -
- ); - } else { - return (

Getting started

To start mapping images to the collation, please upload a manifest in the "Manage Sources" tab.

); - } + {middlePanel} +
+ {sideBacklog} + {imageBacklog} +
+ {mainToolBar} + {imageViewerModal} +
+ ); } } -export default DragDropContext(HTML5Backend)(MapImages); diff --git a/viscoll-app/src/components/imageManager/mapImages/ImageBacklog.js b/viscoll-app/src/components/imageManager/mapImages/ImageBacklog.js new file mode 100644 index 00000000..15fc13a8 --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/ImageBacklog.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import ThumbnailIcon from 'material-ui/svg-icons/editor/insert-photo'; +import IconButton from 'material-ui/IconButton'; +import Add from 'material-ui/svg-icons/content/add-circle-outline'; +import VirtualList from 'react-tiny-virtual-list'; + + +export default class ImageBacklog extends Component { + + renderImageItem = (index, style) => { + const image = this.props.images[index]; + let actionButtons = ( +
+ {event.stopPropagation();this.props.moveItemsToMap([image], "imageMapBoard", "imageBacklog")}} + tabIndex={this.props.tabIndex} + > + + +
+ ); + let activeStyle = {}; + if (this.props.selectedObjects.members.find(item => item.label===image.label && item.manifestID===image.manifestID)) + activeStyle = {backgroundColor: "#4ED6CB"} + return ( +
this.props.handleObjectClick(this.props.id, image, event)} > +
+
{e.stopPropagation();this.props.toggleImageModal(true, image.url)}}> + + + +
+
+ {image.label} +
+
+ {actionButtons} +
+ ); + } + + + render() { + if (this.props.id==="imageMapBoard") { + return ( +
+ this.renderImageItem(index, style)} + overscanCount={10} + estimatedItemSize={400} + /> +
+ ); + } + + // imageBacklog + return ( + this.renderImageItem(index, style)} + overscanCount={10} + estimatedItemSize={400} + /> + ); + + + } +} diff --git a/viscoll-app/src/components/imageManager/mapImages/MapBoard.js b/viscoll-app/src/components/imageManager/mapImages/MapBoard.js new file mode 100644 index 00000000..4bdd7c6a --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/MapBoard.js @@ -0,0 +1,163 @@ +import React, { Component } from 'react'; +import ThumbnailIcon from 'material-ui/svg-icons/editor/insert-photo'; +import IconButton from 'material-ui/IconButton'; +import ArrowDown from 'material-ui/svg-icons/navigation/arrow-downward'; +import ArrowUp from 'material-ui/svg-icons/navigation/arrow-upward'; +import Remove from 'material-ui/svg-icons/content/remove-circle-outline'; +import VirtualList from 'react-tiny-virtual-list'; + + +export default class ImageBin extends Component { + + + + + renderSideItem = (index) => { + const sideID = this.props.sideIDs[index]; + const side = sideID.charAt(0)==="R" ? this.props.Rectos[sideID] : this.props.Versos[sideID]; + const folioNumber = side.folio_number!=="None" ? side.folio_number : ""; + let actionButtons = ( +
event.stopPropagation()}> + this.props.moveItemUpOrDown(sideID, "sideMapBoard", "up")} + disabled={index===0} + tabIndex={this.props.tabIndex} + > + + + this.props.moveItemUpOrDown(sideID, "sideMapBoard", "down")} + disabled={index===this.props.sideIDs.length-1} + tabIndex={this.props.tabIndex} + > + + + this.props.moveItemsToBacklog([sideID], "sideMapBoard", "sideBacklog")} + tabIndex={this.props.tabIndex} + > + + +
+ ); + let activeStyle = {}; + if (this.props.selectedObjects.members.includes(sideID)) + activeStyle = {backgroundColor: "#4ED6CB"} + return ( +
this.props.handleObjectClick("sideMapBoard", sideID, event)}> +
event.stopPropagation()}> + {"Leaf " + side.parentOrder + " " + side.memberType + " (" + folioNumber+")"} +
+ {actionButtons} +
+ ); + } + + renderGhostSideItem = (index) => { + return (
); + } + + renderImageItem = (index) => { + const image = this.props.images[index]; + let actionButtons = ( +
event.stopPropagation()}> + this.props.moveItemUpOrDown(image, "imageMapBoard", "up")} + disabled={index===0} + tabIndex={this.props.tabIndex} + > + + + this.props.moveItemUpOrDown(image, "imageMapBoard", "down")} + tabIndex={this.props.tabIndex} + disabled={index===this.props.images.length-1} + > + + + this.props.moveItemsToBacklog([image], "imageMapBoard", "imageBacklog")} + tabIndex={this.props.tabIndex} + > + + +
+ ); + let activeStyle = {}; + if (this.props.selectedObjects.members.find(item => item.label===image.label && item.manifestID===image.manifestID)) + activeStyle = {backgroundColor: "#4ED6CB"} + return ( +
this.props.handleObjectClick("imageMapBoard", image, event)} > +
+
{e.stopPropagation();this.props.toggleImageModal(true, image.url)}}> + + + +
+
event.stopPropagation()}> + {image.label} +
+
+ {actionButtons} +
+ ); + } + + + renderGhostImageItem = (index) => { + return (
); + } + + + renderItem = (index, style) => { + return ( +
+ {this.props.sideIDs[index] ? + this.renderSideItem(index) + : + this.renderGhostSideItem(index) + } + {this.props.images[index] ? + this.renderImageItem(index) + : + this.renderGhostImageItem(index) + } +
+ ); + } + + + render() { + if (this.props.sideIDs.length===0 && this.props.images.length===0){ + return ( +
Move items from the "backlog" into this area
+ ); + } + + return ( + this.renderItem(index, style)} + overscanCount={10} + estimatedItemSize={100} + /> + ); + + + } +} diff --git a/viscoll-app/src/components/imageManager/mapImages/SideBacklog.js b/viscoll-app/src/components/imageManager/mapImages/SideBacklog.js new file mode 100644 index 00000000..957e27fe --- /dev/null +++ b/viscoll-app/src/components/imageManager/mapImages/SideBacklog.js @@ -0,0 +1,69 @@ +import React, { Component } from 'react'; +import IconButton from 'material-ui/IconButton'; +import Add from 'material-ui/svg-icons/content/add-circle-outline'; +import VirtualList from 'react-tiny-virtual-list'; + + +export default class SideBacklog extends Component { + + renderSideItem = (index, style) => { + const sideID = this.props.sideIDs[index]; + const side = sideID.charAt(0)==="R" ? this.props.Rectos[sideID] : this.props.Versos[sideID]; + const folioNumber = side.folio_number!=="None" ?side.folio_number : ""; + let actionButtons = ( +
event.stopPropagation()}> + this.props.moveItemsToMap([sideID], "sideMapBoard", "sideBacklog")} + tabIndex={this.props.tabIndex} + > + + +
+ ); + let activeStyle = {}; + if (this.props.selectedObjects.members.includes(sideID)) + activeStyle = {backgroundColor: "#4ED6CB"} + return ( +
this.props.handleObjectClick(this.props.id, sideID, event)}> +
+ {"Leaf " + side.parentOrder + " " + side.memberType + " ("+folioNumber+")"} +
+ {actionButtons} +
+ ); + } + + render() { + if (this.props.id==="sideMapBoard") { + return ( +
+ this.renderSideItem(index, style)} + overscanCount={10} + estimatedItemSize={400} + /> +
+ ); + } + + // sideBacklog + return ( + this.renderSideItem(index, style)} + overscanCount={10} + estimatedItemSize={400} + /> + ); + + } +} diff --git a/viscoll-app/src/components/infoBox/GroupInfoBox.js b/viscoll-app/src/components/infoBox/GroupInfoBox.js index 3caffbe8..1069a97e 100644 --- a/viscoll-app/src/components/infoBox/GroupInfoBox.js +++ b/viscoll-app/src/components/infoBox/GroupInfoBox.js @@ -18,19 +18,19 @@ import IconAdd from 'material-ui/svg-icons/content/add'; import IconPencil from 'material-ui/svg-icons/content/create'; import Avatar from 'material-ui/Avatar'; import AddNote from './dialog/AddNote'; -import NoteDialog from './dialog/NoteDialog'; +import VisualizationDialog from './dialog/VisualizationDialog'; export default class GroupInfoBox extends React.Component { constructor(props) { super(props); this.state = { - activeNote: null, ...this.emptyAttributeState(), ...this.otherAttributeStates(), ...this.visibilityHoverState(), addButtonPopoverOpen: false, addGroupDialogOpen: false, addLeafDialogOpen: false, + visualizationDialogActive: "", } this.batchSubmit = this.batchSubmit.bind(this); this.hasActiveAttributes = this.hasActiveAttributes.bind(this); @@ -68,15 +68,6 @@ export default class GroupInfoBox extends React.Component { if (this.props.selectedGroups.length < 2) { this.setState({...this.emptyAttributeState()}); } - if (nextProps.commonNotes.length===0) { - this.setState({activeNote:null}); - } - // Update active note - nextProps.commonNotes.forEach((noteID)=> { - if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { - this.setState({activeNote: nextProps.Notes[noteID]}); - } - }); } hasActiveAttributes() { @@ -92,10 +83,12 @@ export default class GroupInfoBox extends React.Component { toggleAddGroupDialog = (value=false) => { this.setState({ addGroupDialogOpen: value, addButtonPopoverOpen: false, }) + if (value===false) this.props.togglePopUp(false); } toggleAddLeafDialog = (value=false) => { this.setState({ addLeafDialogOpen: value, addButtonPopoverOpen: false, }) + if (value===false) this.props.togglePopUp(false); } handleAddButtonTouchTap = (event) => { @@ -158,7 +151,7 @@ export default class GroupInfoBox extends React.Component { this.setState({...newAttributeState,...newEditingState}); } - singleSubmit(attributeName, value) { + singleSubmit = (attributeName, value) => { let group = {}; group[attributeName] = value; let id = this.props.selectedGroups[0]; @@ -211,10 +204,12 @@ export default class GroupInfoBox extends React.Component { key={note.id} style={{marginRight:4, marginBottom:4}} onRequestDelete={deleteFn} - onClick={()=>this.setState({activeNote: note})} + onClick={()=>this.props.openNoteDialog(note)} + tabIndex={this.props.tabIndex} > {note.title} - ); + + ); } return chips; } @@ -223,21 +218,60 @@ export default class GroupInfoBox extends React.Component { this.setState({activeNote: null}); } - toggleTacketing = () => { - this.props.action.toggleTacket(this.props.selectedGroups[0]); + toggleTacketDrawing = (e) => { + e.stopPropagation(); + this.props.action.toggleVisualizationDrawing({type:"tacketed", value: this.props.selectedGroups[0]}); + this.handleAddButtonRequestClose(); + } + + toggleSewingDrawing = (e) => { + e.stopPropagation(); + this.props.action.toggleVisualizationDrawing({type:"sewing", value: this.props.selectedGroups[0]}); this.handleAddButtonRequestClose(); } + + handleTacketSewingChange = (type, leafID, index) => { + const targetGroup = this.props.Groups[this.props.selectedGroups[0]]; + const value = leafID==="spine"? null : leafID; + let groupPayload = {}; + groupPayload[type] = targetGroup[type]; + if (groupPayload[type].length===2) { + groupPayload[type][index] = value; + } else if (groupPayload[type].length===1 && index===0) { + // Array has one item, which is the endleaf. Insert startleaf ID + groupPayload[type].splice(index, 0, value); + } else if (groupPayload[type].length===1 && index===1) { + // Array has one item, which is the endleaf. Replace endleaf ID + groupPayload[type] = [value]; + } + this.props.action.updateGroup(targetGroup.id, groupPayload); + } + + deleteTacket = () => { + this.singleSubmit("tacketed", []); + } + + deleteSewing = () => { + this.singleSubmit("sewing", []); + } + + toggleVisualizationDialog = (value) => { + this.setState({visualizationDialogActive:value}); + if (value==="") { + this.props.togglePopUp(false); + } else { + this.props.togglePopUp(true); + } + } + render() { const isBatch = this.props.selectedGroups.length > 1; let attributeDivs = []; let groupAttributes = this.getAttributeValues(); this.props.defaultAttributes.forEach((attributeDict)=> { - // Generate checkbox if we're in batch edit mode let label = attributeDict.displayName; - // Generate eye toggle checkbox let eyeCheckbox = ""; - let eyeStyle = {}; let eyeIsChecked = this.props.visibleAttributes[attributeDict.name]; if (this.props.viewMode!=="TABULAR") { @@ -250,14 +284,16 @@ export default class GroupInfoBox extends React.Component { eyeCheckbox =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px",...eyeStyle}} iconStyle={{marginRight:"10px",...eyeStyle}} checked={eyeIsChecked} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
0?{display:"none"}:{}}> {eyeIsChecked? @@ -267,27 +303,31 @@ export default class GroupInfoBox extends React.Component {
label = this.toggleCheckbox(attributeDict.name,value)} + onClick={(event,value)=>this.toggleCheckbox(attributeDict.name,value)} labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} checked={this.state["batch_"+attributeDict.name]} style={{display:"inline-block",width:"25px"}} iconStyle={{marginRight:"10px"}} + tabIndex={this.props.tabIndex} />; } else { // In single edit - display eye icon with label label =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("group", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px",...eyeStyle}} iconStyle={{marginRight:"10px", color:"gray",...eyeStyle}} checked={eyeIsChecked} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
0?{display:"none"}:{}}> {eyeIsChecked? @@ -316,10 +356,12 @@ export default class GroupInfoBox extends React.Component { value = groupAttributes[attributeDict.name]; } input = (this.dropDownChange(e,i,v,attributeDict.name)} fullWidth={true} disabled={isBatch && !this.state["batch_"+attributeDict.name]} + tabIndex={this.props.tabIndex} > {menuItems} @@ -331,16 +373,20 @@ export default class GroupInfoBox extends React.Component { textboxButtons = (
} style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.textSubmit(e,attributeDict.name)} + onClick={(e)=>this.textSubmit(e,attributeDict.name)} + tabIndex={this.props.tabIndex} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.textCancel(e,attributeDict.name)} + onClick={(e)=>this.textCancel(e,attributeDict.name)} + tabIndex={this.props.tabIndex} />
); @@ -354,11 +400,13 @@ export default class GroupInfoBox extends React.Component { input = (
this.textSubmit(e,attributeDict.name)}> this.onTextboxChange(v,attributeDict.name)} disabled={isBatch && !this.state["batch_"+attributeDict.name]} + tabIndex={this.props.tabIndex} /> {textboxButtons} @@ -387,9 +435,10 @@ export default class GroupInfoBox extends React.Component { if (isBatch && this.hasActiveAttributes()) { submitBtn = } let addBtn = ""; @@ -404,16 +453,17 @@ export default class GroupInfoBox extends React.Component { animation={PopoverAnimationVertical} > - this.toggleAddGroupDialog(true)} /> - this.toggleAddLeafDialog(true)}/> - + {this.props.togglePopUp(true);this.toggleAddGroupDialog(true)}} /> + {this.props.togglePopUp(true);this.toggleAddLeafDialog(true)}}/> + addBtn = } let deleteBtn = @@ -423,33 +473,93 @@ export default class GroupInfoBox extends React.Component { selectedObjects={this.props.selectedGroups} memberType="Group" Groups={this.props.Groups} - + tabIndex={this.props.tabIndex} + togglePopUp={this.props.togglePopUp} /> - let attributeTacket = + let attributeSewing = ""; + const sewing = this.props.Groups[this.props.selectedGroups[0]].sewing; + if (this.props.selectedGroups.length===1 && sewing.length===0 && this.props.viewMode!=="VIEWING") { + attributeSewing =
+
+ {if(this.props.viewMode==="TABULAR"){this.toggleVisualizationDialog("sewing")}else{this.toggleSewingDrawing(e)}}} + tabIndex={this.props.tabIndex} + > + + +
+

Sewing

+
+ } else if (this.props.selectedGroups.length===1 && sewing.length>0) { + attributeSewing =
-
- - +
+

Sewing

+ {this.deleteSewing()}:null} + onClick={()=>{if(this.props.viewMode!=="VIEWING")this.toggleVisualizationDialog("sewing")}} + tabIndex={this.props.tabIndex} + > +
+
+ {sewing.length===1? + "Spine to Leaf " + this.props.Leafs[sewing[0]].order : + "Leaf " + this.props.Leafs[sewing[0]].order + " to Leaf " + this.props.Leafs[sewing[1]].order + } +
+
+
+ }/> +
+
+
+
+
+
+
+ } + const tacketed = this.props.Groups[this.props.selectedGroups[0]].tacketed; + let attributeTacket = ""; + if (this.props.selectedGroups.length===1 && tacketed.length===0 && this.props.viewMode!=="VIEWING") { + attributeTacket =
+
+ {if(this.props.viewMode==="VISUAL"){this.toggleTacketDrawing(e)}else{this.toggleVisualizationDialog("tacketed")}}} + tabIndex={this.props.tabIndex} + > +
-

Tacket

+

Tacket

- if (!this.state.isBatch && this.props.Groups[this.props.selectedGroups[0]].tacketed!=="") { + } else if (this.props.selectedGroups.length===1 && tacketed.length>0) { attributeTacket =

Tacket

{this.singleSubmit("tacketed", "")}:null} + onRequestDelete={this.props.viewMode!=="VIEWING"?()=>{this.deleteTacket()}:null} + onClick={()=>{if(this.props.viewMode!=="VIEWING")this.toggleVisualizationDialog("tacketed")}} + tabIndex={this.props.tabIndex} >
- {"Tacketed to Leaf " + this.props.Leafs[this.props.Groups[this.props.selectedGroups[0]].tacketed].order } + {tacketed.length===1? + "Spine to Leaf " + this.props.Leafs[tacketed[0]].order : + "Leaf " + this.props.Leafs[tacketed[0]].order + " to Leaf " + this.props.Leafs[tacketed[1]].order + }
- }/> + }/>
@@ -472,12 +582,15 @@ export default class GroupInfoBox extends React.Component { createAndAttachNote: this.props.action.createAndAttachNote }} noteTypes={this.props.noteTypes} + tabIndex={this.props.tabIndex} + togglePopUp={this.props.togglePopUp} /> : ""}

{this.props.selectedGroups.length>1?"Notes in common" : "Notes"}

{notes}
+ {attributeSewing} {attributeTacket} {submitBtn} {addButtonPopover} @@ -503,28 +616,20 @@ export default class GroupInfoBox extends React.Component { action={{addLeafs: this.props.action.addLeafs}} open={this.state.addLeafDialogOpen} closeDialog={this.toggleAddLeafDialog} - /> - + this.toggleVisualizationDialog("")} + group={this.props.selectedGroups.length>0? this.props.Groups[this.props.selectedGroups[0]] : null} + tacketed={this.props.Groups[this.props.selectedGroups[0]].tacketed} + sewing={this.props.Groups[this.props.selectedGroups[0]].sewing} Leafs={this.props.Leafs} - Rectos={this.props.Rectos} - Versos={this.props.Versos} - isReadOnly={this.props.isReadOnly} + activeGroup={this.props.Groups[this.props.selectedGroups[0]]} + handleTacketSewingChange={this.handleTacketSewingChange} + delete={()=>this.singleSubmit(this.state.visualizationDialogActive, [])} + updateGroup={this.singleSubmit} + popUpActive={this.props.popUpActive} />
); diff --git a/viscoll-app/src/components/infoBox/LeafInfoBox.js b/viscoll-app/src/components/infoBox/LeafInfoBox.js index 86fc3ef0..ce9e892d 100644 --- a/viscoll-app/src/components/infoBox/LeafInfoBox.js +++ b/viscoll-app/src/components/infoBox/LeafInfoBox.js @@ -9,10 +9,9 @@ import Visibility from 'material-ui/svg-icons/action/visibility'; import VisibilityOff from 'material-ui/svg-icons/action/visibility-off'; import {getLeafsOfGroup} from '../../helpers/getLeafsOfGroup'; import Chip from 'material-ui/Chip'; +import Dialog from 'material-ui/Dialog'; import AddNote from './dialog/AddNote'; -import NoteDialog from './dialog/NoteDialog'; import ImageViewer from "../global/ImageViewer"; -import Dialog from 'material-ui/Dialog'; export default class LeafInfoBox extends React.Component { @@ -21,7 +20,6 @@ export default class LeafInfoBox extends React.Component { this.state = { imageModalOpen: false, - activeNote: null, isBatch: this.props.selectedLeaves.length>1, ...this.emptyAttributeState(), ...this.batchAttributeToggleState(), @@ -68,15 +66,6 @@ export default class LeafInfoBox extends React.Component { if (!this.state.isBatch) { this.setState({...this.emptyAttributeState()}); } - if (nextProps.commonNotes.length===0) { - this.setState({activeNote:null}); - } - // Update active note - nextProps.commonNotes.forEach((noteID)=> { - if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { - this.setState({activeNote: nextProps.Notes[noteID]}); - } - }); } hasActiveAttributes = () => { @@ -181,7 +170,8 @@ export default class LeafInfoBox extends React.Component { key={note.id} style={{marginRight:4, marginBottom:4}} onRequestDelete={deleteFn} - onClick={()=>this.setState({activeNote: note})} + onClick={()=>this.props.openNoteDialog(note)} + tabIndex={this.props.tabIndex} > {note.title} ); @@ -191,10 +181,12 @@ export default class LeafInfoBox extends React.Component { closeNoteDialog = () => { this.setState({activeNote:null}); + this.props.togglePopUp(false); } toggleImageModal = (imageModalOpen) => { this.setState({imageModalOpen}) + this.props.togglePopUp(imageModalOpen); } render() { @@ -218,18 +210,19 @@ export default class LeafInfoBox extends React.Component { // Generate eye toggle checkbox let eyeCheckbox = ""; if (this.props.viewMode==="TABULAR" && this.state.isBatch) { - eyeCheckbox =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px"}} iconStyle={{marginRight:"10px"}} checked={this.props.visibleAttributes[attributeDict.name]} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
{this.props.visibleAttributes[attributeDict.name]? @@ -243,16 +236,18 @@ export default class LeafInfoBox extends React.Component { label =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("leaf", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px"}} checked={this.props.visibleAttributes[attributeDict.name]} iconStyle={{marginRight:"10px", color:"gray"}} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
{this.props.visibleAttributes[attributeDict.name]? @@ -265,14 +260,16 @@ export default class LeafInfoBox extends React.Component { if (this.state.isBatch && !this.props.isReadOnly) { // In batch edit for either edit modes label = this.toggleCheckbox(attributeDict.name,value)} + onClick={(event,value)=>this.toggleCheckbox(attributeDict.name,value)} labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} checked={this.state["batch_"+attributeDict.name]} style={{display:"inline-block",width:"25px"}} iconStyle={{marginRight:"10px"}} - disabled={(attributeDict.name==="conjoined_leaf_order"||attributeDict.name.includes("attached_to"))} + disabled={(attributeDict.name==="conjoined_leaf_order"||attributeDict.name.includes("attached_to"))} + tabIndex={this.props.tabIndex} />; } let input = leafAttributes[attributeDict.name]; @@ -290,10 +287,12 @@ export default class LeafInfoBox extends React.Component { }); input = this.onConjoinChange(e,i,activeLeaf,v)} fullWidth={true} disabled={this.state.isBatch} + tabIndex={this.props.tabIndex} > {menuItems} @@ -314,10 +313,12 @@ export default class LeafInfoBox extends React.Component { } input = this.dropDownChange(e,i,v,attributeDict.name)} fullWidth={true} disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + tabIndex={this.props.tabIndex} > {menuItems} @@ -342,9 +343,10 @@ export default class LeafInfoBox extends React.Component { if (this.state.isBatch && this.hasActiveAttributes()) { submitBtn = } let addBtn = ""; @@ -354,6 +356,8 @@ export default class LeafInfoBox extends React.Component { Leafs={this.props.Leafs} selectedLeaves={this.props.selectedLeaves} projectID={this.props.projectID} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.tabIndex} /> } let deleteBtn = ( @@ -364,15 +368,18 @@ export default class LeafInfoBox extends React.Component { memberType="Leaf" Leafs={this.props.Leafs} Groups={this.props.Groups} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.tabIndex} /> ); let conjoinButton = ( ); if (this.props.selectedLeaves.length<2){ @@ -398,30 +405,42 @@ export default class LeafInfoBox extends React.Component { imageModalContent = (); if (rectoURL) { imageThumbnails.push( -
+
+ ) } if (versoURL) { imageThumbnails.push( -
+
+ ) } } @@ -445,6 +464,8 @@ export default class LeafInfoBox extends React.Component { createAndAttachNote: this.props.action.createAndAttachNote }} noteTypes={this.props.noteTypes} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.tabIndex} />}

@@ -466,29 +487,7 @@ export default class LeafInfoBox extends React.Component { {deleteBtn}

} - - + 1, ...this.emptyAttributeState(), @@ -74,15 +72,6 @@ export default class SideInfoBox extends React.Component { if (!this.state.isBatch) { this.setState({...this.emptyAttributeState()}); } - if (nextProps.commonNotes.length===0) { - this.setState({activeNote:null}); - } - // Update active note - nextProps.commonNotes.forEach((noteID)=> { - if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { - this.setState({activeNote: nextProps.Notes[noteID]}); - } - }); } @@ -196,7 +185,8 @@ export default class SideInfoBox extends React.Component { key={note.id} style={{marginRight:4, marginBottom:4}} onRequestDelete={deleteFn} - onClick={()=>{this.setState({activeNote: note})}} + onClick={()=>this.props.openNoteDialog(note)} + tabIndex={this.props.tabIndex} > {note.title} ); @@ -236,14 +226,16 @@ export default class SideInfoBox extends React.Component { eyeCheckbox =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px",...eyeStyle}} iconStyle={{marginRight:"10px",...eyeStyle}} checked={eyeIsChecked} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
0?{display:"none"}:{}}> {eyeIsChecked? @@ -253,28 +245,32 @@ export default class SideInfoBox extends React.Component {
label = this.toggleCheckbox(attributeDict.name,value)} + onClick={(event,value)=>this.toggleCheckbox(attributeDict.name,value)} labelStyle={!this.state["batch_"+attributeDict.name]?{color:"gray"}:{}} checked={this.state["batch_"+attributeDict.name]} style={{display:"inline-block",width:"25px"}} iconStyle={{marginRight:"10px"}} disabled={this.state.isBatch && (attributeDict.name==="folio_number"||attributeDict.name==="uri")} + tabIndex={this.props.tabIndex} />; } else { // In single edit, display eye icon with label (no checkbox) label =
} uncheckedIcon={} - onCheck={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} + onClick={(event,value)=>this.props.action.toggleVisibility("side", attributeDict.name, !this.props.visibleAttributes[attributeDict.name])} style={{display:"inline-block",width:"25px",...eyeStyle}} iconStyle={{marginRight:"10px", color:"gray",...eyeStyle}} checked={eyeIsChecked} onMouseEnter={()=>{this.setState({["visibility_hover_"+attributeDict.name]:true})}} onMouseOut={()=>{this.setState({["visibility_hover_"+attributeDict.name]:false})}} + tabIndex={this.props.tabIndex} />
0?{display:"none"}:{}}> {eyeIsChecked? @@ -304,10 +300,12 @@ export default class SideInfoBox extends React.Component { value = sideAttributes[attributeDict.name]; } input = (this.dropDownChange(e,i,v,attributeDict.name)} fullWidth={true} disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + tabIndex={this.props.tabIndex} > {menuItems} @@ -319,16 +317,20 @@ export default class SideInfoBox extends React.Component { textboxButtons = (
} style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.textSubmit(e,attributeDict.name)} + onClick={(e)=>this.textSubmit(e,attributeDict.name)} + tabIndex={this.props.tabIndex} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.textCancel(e,attributeDict.name)} + onClick={(e)=>this.textCancel(e,attributeDict.name)} + tabIndex={this.props.tabIndex} />
); @@ -336,17 +338,19 @@ export default class SideInfoBox extends React.Component { let value = "Keep same"; if (this.state["editing_"+attributeDict.name]) { value = this.state[attributeDict.name]; - } else if (sideAttributes[attributeDict.name]) { + } else if (sideAttributes[attributeDict.name]!==null) { value = sideAttributes[attributeDict.name]; } input = (
this.textSubmit(e,attributeDict.name)}> this.onTextboxChange(v,attributeDict.name)} disabled={this.state.isBatch && !this.state["batch_"+attributeDict.name]} + tabIndex={this.props.tabIndex} /> {textboxButtons} @@ -387,6 +391,8 @@ export default class SideInfoBox extends React.Component { createAndAttachNote: this.props.action.createAndAttachNote }} noteTypes={this.props.noteTypes} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.tabIndex} />}

{Object.keys(this.props.selectedSides).length>1?"Notes in common" : "Notes"}

@@ -400,8 +406,9 @@ export default class SideInfoBox extends React.Component { if (this.state.isBatch && this.hasActiveAttributes()) { submitBtn = } @@ -417,14 +424,20 @@ export default class SideInfoBox extends React.Component { imageModalContent = (); if (side.image.url){ imageThumbnail.push( -
+
+ ) } } @@ -433,33 +446,11 @@ export default class SideInfoBox extends React.Component { return (
{attributeDivs} -
+
{imageThumbnail}
{notesDiv} {submitBtn} - { + incrementNumber = (name, min, max, e) => { + if (e) e.preventDefault(); let newCount = 0; if (!this.isNormalInteger(this.state[name])) { newCount = min; @@ -63,7 +68,8 @@ export default class AddGroupDialog extends React.Component { * @param {number} max * @public */ - decrementNumber = (name, min, max) => { + decrementNumber = (name, min, max, e) => { + if (e) e.preventDefault(); let newCount = Math.min(max, Math.max(min, this.state[name]-1)); let newState = {errorText:{}}; newState[name]=(isNaN(newCount))?min:newCount; @@ -281,7 +287,7 @@ export default class AddGroupDialog extends React.Component { conjoin: false, oddLeaf: 2, copies: 1, - location: "", + location: this.props.selectedGroups.length>0?"":"inside", errorText: { numberOfGroups: "", numberOfLeaves: "", @@ -295,7 +301,7 @@ export default class AddGroupDialog extends React.Component { const actions = [ {this.resetForm();this.props.closeDialog()}} + onClick={()=>{this.props.closeDialog()}} style={{width:"49%", marginRight:"1%",border:"1px solid #ddd"}} />, , @@ -327,20 +333,23 @@ export default class AddGroupDialog extends React.Component {
this.onNumberChange("numberOfLeaves", v)} - style={{width:"100px"}} - inputStyle={{textAlign:"center"}} + aria-label="Number of leaves" + name="numberOfLeaves" + value={this.state.numberOfLeaves} + errorText={this.state.errorText.numberOfLeaves} + onChange={(e,v)=>this.onNumberChange("numberOfLeaves", v)} + style={{width:"100px"}} + inputStyle={{textAlign:"center"}} /> this.decrementNumber("numberOfLeaves", 1, 999)} + onClick={(e) => this.decrementNumber("numberOfLeaves", 1, 999, e)} + aria-label="Decrement number of leaves" > this.incrementNumber("numberOfLeaves", 1, 999)} + onClick={(e) => this.incrementNumber("numberOfLeaves", 1, 999, e)} + aria-label="Increment number of leaves" > @@ -356,6 +365,7 @@ export default class AddGroupDialog extends React.Component {
this.onToggleCheckbox("conjoin", v)} /> @@ -370,6 +380,7 @@ export default class AddGroupDialog extends React.Component {
this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + onClick={(e) => this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves, e)} + aria-label="Decrement leaf number" > this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + onClick={(e) => this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves, e)} + aria-label="Increment leaf number" > @@ -397,6 +410,7 @@ export default class AddGroupDialog extends React.Component {
this.decrementNumber("numberOfGroups", 1, 999)} + aria-label="Decrement number of groups" + name="Decrement number of groups" + onClick={(e) => this.decrementNumber("numberOfGroups", 1, 999, e)} > this.incrementNumber("numberOfGroups", 1, 999)} + aria-label="Increment number of groups" + name="Increment number of groups" + onClick={(e) => this.incrementNumber("numberOfGroups", 1, 999, e)} > @@ -420,16 +438,20 @@ export default class AddGroupDialog extends React.Component { let radioButtonGroupHeader =

Add new group(s)

; let radioButtonGroup = this.onLocationChange(v)}>
this.onToggleCheckbox("hasLeaves", v)} + aria-label="Add leaves inside" + checked={this.state.hasLeaves} + onCheck={(e,v)=>this.onToggleCheckbox("hasLeaves", v)} />
: ""; diff --git a/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js index cdb040a7..dcdc6da9 100644 --- a/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js +++ b/viscoll-app/src/components/infoBox/dialog/AddLeafDialog.js @@ -33,12 +33,14 @@ export default class AddLeafDialog extends React.Component { /** Open this modal component */ handleOpen = () => { this.setState({open: true}); + this.props.togglePopUp(true); }; /** Close this modal component */ handleClose = () => { this.clearForm(); this.setState({open: false}); + this.props.togglePopUp(false); }; /** @@ -50,7 +52,8 @@ export default class AddLeafDialog extends React.Component { * @param {number} max * @public */ - incrementNumber = (name, min, max) => { + incrementNumber = (name, min, max, e) => { + if (e) e.preventDefault(); let newCount = 0; if (!this.isNormalInteger(this.state[name])) { newCount = min; @@ -72,7 +75,8 @@ export default class AddLeafDialog extends React.Component { * @param {number} max * @public */ - decrementNumber = (name, min, max) => { + decrementNumber = (name, min, max, e) => { + if (e) e.preventDefault(); let newCount = Math.min(max, Math.max(min, this.state[name]-1)); let newState = {errorText:{}}; newState[name]=(isNaN(newCount))?1:newCount; @@ -201,14 +205,14 @@ export default class AddLeafDialog extends React.Component { const actions = [ , , @@ -232,6 +236,7 @@ export default class AddLeafDialog extends React.Component {
this.decrementNumber("numberOfLeaves", 1, 999)} + aria-label="Decrement number of leaves" + name="Decrement number of leaves" + onClick={(e) => this.decrementNumber("numberOfLeaves", 1, 999, e)} > this.incrementNumber("numberOfLeaves", 1, 999)} + aria-label="Increment number of leaves" + name="Increment number of leaves" + onClick={(e) => this.incrementNumber("numberOfLeaves", 1, 999, e)} > @@ -261,6 +270,7 @@ export default class AddLeafDialog extends React.Component {
this.onToggleConjoin(v)} checked={this.state.conjoin && this.state.numberOfLeaves>1} style={{verticalAlign:"bottom"}} @@ -276,6 +286,7 @@ export default class AddLeafDialog extends React.Component {
this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + aria-label="Decrement leaf number" + name="Decrement leaf number" + onClick={(e) => this.decrementNumber("oddLeaf", 1, this.state.numberOfLeaves, e)} > this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves)} + aria-label="Increment leaf number" + name="Increment leaf number" + onClick={(e) => this.incrementNumber("oddLeaf", 1, this.state.numberOfLeaves, e)} > @@ -331,18 +346,18 @@ export default class AddLeafDialog extends React.Component { defaultSelected={defaultAddLocation} onChange={(e,v)=>this.onLocationChange(v)} > - + - {disabledAddAbove}
@@ -371,8 +386,9 @@ export default class AddLeafDialog extends React.Component { {dialog}
diff --git a/viscoll-app/src/components/infoBox/dialog/AddNote.js b/viscoll-app/src/components/infoBox/dialog/AddNote.js index aae28b97..154708ad 100644 --- a/viscoll-app/src/components/infoBox/dialog/AddNote.js +++ b/viscoll-app/src/components/infoBox/dialog/AddNote.js @@ -8,6 +8,8 @@ import AutoComplete from 'material-ui/AutoComplete'; import SelectField from 'material-ui/SelectField'; import MenuItem from 'material-ui/MenuItem'; import TextField from 'material-ui/TextField'; +import Checkbox from 'material-ui/Checkbox'; + /** Dialog to add a note to an object (leaf, side, or group). This component is used in the visual and tabular edit modes. */ export default class AddNote extends React.Component { @@ -17,6 +19,7 @@ export default class AddNote extends React.Component { open: false, type: '', description:'', + show: false, searchText: '', noteID: null, }; @@ -28,14 +31,17 @@ export default class AddNote extends React.Component { open: true, type: '', description:'', + show: false, searchText: '', noteID: null, }); + this.props.togglePopUp(true); }; /** Close this modal component */ handleClose = () => { this.setState({open: false}); + this.props.togglePopUp(false); }; handleUpdateInput = (searchText) => { @@ -51,7 +57,7 @@ export default class AddNote extends React.Component { let noteID = null; for (let id in this.props.Notes){ const note = this.props.Notes[id]; - if (note.title===request) { console.log("here");noteID = note.id;} + if (note.title===request) { noteID = note.id;} } this.setState({noteID}, ()=>{ if (noteID) this.submit() @@ -73,7 +79,7 @@ export default class AddNote extends React.Component { this.props.action.linkNote(noteID); } else { // Did not find note, so create and attach new note to object - this.props.action.createAndAttachNote(this.state.searchText, this.state.type, this.state.description); + this.props.action.createAndAttachNote(this.state.searchText, this.state.type, this.state.description, this.state.show); } } this.handleClose(); @@ -121,14 +127,14 @@ export default class AddNote extends React.Component { const actions = [ , , @@ -156,6 +162,18 @@ export default class AddNote extends React.Component { fullWidth style={{marginTop:-20}} /> +
+ Show in diagram +
+
+ this.onChange("show",v)} + /> +
); } @@ -192,8 +210,12 @@ export default class AddNote extends React.Component { return (
- - + + {dialog}
diff --git a/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js index bb9f0235..9da6723e 100644 --- a/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js +++ b/viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.js @@ -16,6 +16,7 @@ export default class DeleteConfirmationDialog extends React.Component { */ handleOpen = () => { this.setState({open: true}); + this.props.togglePopUp(true); }; /** @@ -24,6 +25,7 @@ export default class DeleteConfirmationDialog extends React.Component { */ handleClose = () => { this.setState({open: false}); + this.props.togglePopUp(false); }; containsTacketedLeaf = () => { @@ -64,7 +66,8 @@ export default class DeleteConfirmationDialog extends React.Component { * Submit delete request and close dialog * @public */ - submit = () => { + submit = (e) => { + if (e) e.preventDefault(); if (this.props.selectedObjects.length===1) { // handle single delete let id = this.props.selectedObjects[0] @@ -87,13 +90,13 @@ export default class DeleteConfirmationDialog extends React.Component { , , @@ -104,10 +107,11 @@ export default class DeleteConfirmationDialog extends React.Component { { + this.setState({[type]:value}); + } + + renderMenuItem = (item, index) => { + if (item.id) { + return + } else { + return + } + } + + onCreate = () => { + let attributeValue = this.state.startLeaf==="spine"?[this.state.endLeaf]:[this.state.startLeaf,this.state.endLeaf]; + this.props.updateGroup(this.props.type, attributeValue); + this.props.closeDialog(); + } + + render() { + let isCreateAction = (this.props.type!=="" && this.props.group[this.props.type].length===0); + const actions = [ + {this.props.closeDialog()}} + />, + , + {this.props.delete(); this.props.closeDialog()}} + />, + ]; + let selectFields = []; + const leafMembersOfCurrentGroup = getLeafsOfGroup(this.props.activeGroup, this.props.Leafs); + if (isCreateAction) { + // Create new sewing/tacket + let selectField1 = ( + this.handleChange("startLeaf",v)} + fullWidth + > + {leafMembersOfCurrentGroup.map((v,i,a)=>this.renderMenuItem(v,0))} + ) + let selectField2 = ( + this.handleChange("endLeaf",v)} + fullWidth + > + {leafMembersOfCurrentGroup.filter((item)=>(item.id!==null&&(this.state.startLeaf===null||this.state.startLeaf==="spine"|| item.order>this.props.Leafs[this.state.startLeaf].order))).map((v,i,a)=>this.renderMenuItem(v,1))} + ) + selectFields.push(selectField1); + selectFields.push(selectField2); + } else if (this.props.type!=="" && this.props.group[this.props.type].length>0) { + // Edit existing sewing/tacket + + const leafStart = this.props.group[this.props.type].length===2? this.props.Leafs[this.props.group[this.props.type][0]] : null; + const leafEnd = this.props.group[this.props.type].length===2? this.props.Leafs[this.props.group[this.props.type][1]] : this.props.Leafs[this.props.group[this.props.type][0]]; + + let selectField1 = ( + this.props.handleTacketSewingChange(this.props.type, v, 0)} + fullWidth + > + {this.renderMenuItem({id:null},0)} + {leafMembersOfCurrentGroup.filter((item)=>(item.id!==null && item.orderthis.renderMenuItem(v,0))} + ) + let selectField2 = ( + this.props.handleTacketSewingChange(this.props.type, v, 1)} + fullWidth + > + {leafMembersOfCurrentGroup.filter((item)=>(leafStart===null && item.id!==null)||(leafStart!==null && item.id!==null && item.order>leafStart.order)).map((v,i,a)=>this.renderMenuItem(v,1))} + ) + selectFields.push(selectField1); + selectFields.push(selectField2); + } + + return ( +
+ this.props.closeDialog()} + contentStyle={{width: "30%", minWidth:"320px", maxWidth:"450px"}} + > + {selectFields} + +
+ ); + } +} \ No newline at end of file diff --git a/viscoll-app/src/components/notesManager/DeleteConfirmation.js b/viscoll-app/src/components/notesManager/DeleteConfirmation.js index 54b5bf6a..e6f54ca3 100644 --- a/viscoll-app/src/components/notesManager/DeleteConfirmation.js +++ b/viscoll-app/src/components/notesManager/DeleteConfirmation.js @@ -17,6 +17,7 @@ export default class DeleteConfirmation extends React.Component { */ handleOpen = () => { this.setState({open: true}); + this.props.togglePopUp(true); }; /** * Close the dialog @@ -24,6 +25,7 @@ export default class DeleteConfirmation extends React.Component { */ handleClose = () => { this.setState({open: false}); + this.props.togglePopUp(false); }; submit = () => { @@ -40,31 +42,36 @@ export default class DeleteConfirmation extends React.Component { , , ]; let deleteIcon =
let message = "This note will be removed from all groups/sides/leaves that have this note."; if (this.props.item==="note type") { deleteIcon = diff --git a/viscoll-app/src/components/notesManager/EditNoteForm.js b/viscoll-app/src/components/notesManager/EditNoteForm.js index 378c0180..4282e932 100644 --- a/viscoll-app/src/components/notesManager/EditNoteForm.js +++ b/viscoll-app/src/components/notesManager/EditNoteForm.js @@ -230,6 +230,7 @@ export default class EditNoteForm extends Component { return (
} style={{minWidth:"60px",marginLeft:"5px"}} @@ -238,10 +239,11 @@ export default class EditNoteForm extends Component { disabled={name==="title" && this.state.errors.title!==""} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.onCancelUpdate(name)} + onClick={(e)=>this.onCancelUpdate(name)} />
) @@ -307,7 +309,8 @@ export default class EditNoteForm extends Component { key={id} style={{marginRight:4, marginBottom:4}} onRequestDelete={deleteFn} - onClick={()=>{}} + onKeyPress={(e)=>{if(e.key===" "||e.key==="Enter"){deleteFn()}}} + tabIndex={this.props.tabIndex} > {name} @@ -327,6 +330,7 @@ export default class EditNoteForm extends Component { this.updatedObjects(selected, "Group")} selectedItems={this.props.linkedGroups} + tabIndex={this.props.tabIndex} > {Object.keys(this.props.Groups).map((itemID)=>this.renderMenuItem(itemID, "Group"))} @@ -338,6 +342,7 @@ export default class EditNoteForm extends Component { this.updatedObjects(selected, "Leaf")} selectedItems={this.props.linkedLeaves} + tabIndex={this.props.tabIndex} > {Object.keys(this.props.Leafs).map((itemID)=>this.renderMenuItem(itemID, "Leaf"))} @@ -349,6 +354,7 @@ export default class EditNoteForm extends Component { this.updatedObjects(selected, "Side")} selectedItems={this.props.linkedSides} + tabIndex={this.props.tabIndex} > {Object.keys(this.props.Sides).map((itemID, index)=>this.renderMenuItem(itemID, "Side", index))} @@ -372,11 +378,13 @@ export default class EditNoteForm extends Component { {this.props.isReadOnly?"":

Attach a new item

this.setState({linkType:v})} value={this.state.linkType} style={{marginTop:"-2em",width:120}} + tabIndex={this.props.tabIndex} > {["Group", "Leaf", "Side"].map((type) => { return ; @@ -391,6 +399,8 @@ export default class EditNoteForm extends Component { item={this.props.note?"note": ""} noteID={this.props.note? this.props.note.id : ""} action={{deleteNote: this.props.action.deleteNote}} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.tabIndex} />
} @@ -398,58 +408,68 @@ export default class EditNoteForm extends Component {

{title}

-
+
Title
{this.props.isReadOnly?
{this.state.title}
:
this.update(e, "title")}> this.onChange("title",v)} - fullWidth + aria-labelledby="noteTitleLabel" + name="title" + value={this.state.title} + errorText={this.state.errors.title} + onChange={(e,v)=>this.onChange("title",v)} + fullWidth + autoFocus + aria-invalid={this.state.errors.title.length>0} + tabIndex={this.props.tabIndex} /> {this.renderSubmitButtons("title")} }
-
+
Type
{this.props.isReadOnly?
{this.state.type}
: this.onChange("type",v)} + tabIndex={this.props.tabIndex} > {this.props.noteTypes.map(this.renderNoteTypes)} }
-
+
Description
{this.props.isReadOnly?
{this.state.description}
:
this.update(e, "description")}> this.onChange("description",v)} multiLine fullWidth + tabIndex={this.props.tabIndex} /> {this.renderSubmitButtons("description")} }
-
+
Show in diagram
this.props.action.updateNote(this.props.note.id, {title:this.state.title,type:this.state.type,description:this.state.description,show:v})} + tabIndex={this.props.tabIndex} />
{linkedObjects} diff --git a/viscoll-app/src/components/notesManager/ManageNotes.js b/viscoll-app/src/components/notesManager/ManageNotes.js index f452aad0..d94620c1 100644 --- a/viscoll-app/src/components/notesManager/ManageNotes.js +++ b/viscoll-app/src/components/notesManager/ManageNotes.js @@ -1,9 +1,11 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import Drawer from 'material-ui/Drawer'; +import RaisedButton from 'material-ui/RaisedButton'; import EditNoteForm from "./EditNoteForm"; import NewNoteForm from "./NewNoteForm"; import Add from "material-ui/svg-icons/content/add" +import {btnGreen,btnMd} from "../../styles/button"; /** Create New Note tab in the Note Manager */ export default class ManageNotes extends Component { @@ -38,13 +40,17 @@ export default class ManageNotes extends Component { renderList = (noteID) => { const note = this.props.Notes[noteID]; return ( -
this.onItemChange(note)} - > - {note.title} -
+ style={{width:"90%"}} + {...btnMd} + label={note.title.length>18? note.title.substring(0,18) + "...": note.title} + labelStyle={{fontSize:15}} + backgroundColor={this.state.activeNote && this.state.activeNote.id===noteID? "#4ED6CB": "#F2F2F2" } + tabIndex={this.props.tabIndex} + /> ); } @@ -147,7 +153,7 @@ export default class ManageNotes extends Component { noteTypes={this.props.noteTypes} /> ); - } else{ + } else { noteForm = ( ); } @@ -183,19 +191,25 @@ export default class ManageNotes extends Component { containerStyle={{top:"56px",left:"18%", height: "93%",background:"#e5e5e5",boxShadow:"none"}} className="notesList" > -
+ this.onItemChange(null)} + style={{width:"90%"}} + {...btnMd} + label="Create New Note" + labelStyle={{fontSize:15}} + icon={} + labelColor={"#ffffff"} + backgroundColor={"#566476"} + tabIndex={this.props.tabIndex} > - Create new note - -
+ {Object.keys(this.props.Notes).map(this.renderList)} +
-
+
{noteForm}
diff --git a/viscoll-app/src/components/notesManager/NewNoteForm.js b/viscoll-app/src/components/notesManager/NewNoteForm.js index ffd2d527..f0ca58b7 100644 --- a/viscoll-app/src/components/notesManager/NewNoteForm.js +++ b/viscoll-app/src/components/notesManager/NewNoteForm.js @@ -4,6 +4,7 @@ import TextField from 'material-ui/TextField'; import SelectField from 'material-ui/SelectField'; import MenuItem from 'material-ui/MenuItem'; import RaisedButton from 'material-ui/RaisedButton'; +import Checkbox from 'material-ui/Checkbox'; /** Create New Note tab in the Note Manager */ @@ -14,6 +15,7 @@ export default class NewNoteForm extends Component { title: "", type: "", description: "", + show: false, errors: { title: "", } @@ -56,6 +58,7 @@ export default class NewNoteForm extends Component { title: this.state.title, type: value, description: this.state.description, + show: this.state.show } if (this.props.note) this.props.action.updateNote(this.props.note.id, editing); @@ -72,6 +75,7 @@ export default class NewNoteForm extends Component { "title": this.state.title, "type": this.state.type, "description": this.state.description, + "show": this.state.show } this.props.action.addNote(note); // Reset form @@ -79,6 +83,7 @@ export default class NewNoteForm extends Component { title: "", type: "", description: "", + show: false, errors: { title: "", } @@ -100,6 +105,7 @@ export default class NewNoteForm extends Component { title:"", type:"", description:"", + show: false } }); } @@ -131,6 +137,11 @@ export default class NewNoteForm extends Component { render() { let title = "Create a new note"; let createButtons =
+ this.reset()} + style={{width:120}} + />   this.create()} disabled={this.state.errors.title!=="" || this.state.type==="" || this.state.title===""} /> -   - this.reset()} - style={{width:120}} - /> + +
return (

{title}

-
+
Title
this.onChange("title",v)} fullWidth + aria-invalid={this.state.errors.title.length>0} />
-
+
Type
this.onChange("type",v)} > {this.props.noteTypes.map(this.renderNoteTypes)}
-
+
Description
this.onChange("description",v)} @@ -185,6 +196,19 @@ export default class NewNoteForm extends Component { fullWidth />
+
+ Show in diagram +
+
+ this.onChange("show",v)} + /> +
{createButtons}
diff --git a/viscoll-app/src/components/notesManager/NoteType.js b/viscoll-app/src/components/notesManager/NoteType.js index aa663de6..a0a25f17 100644 --- a/viscoll-app/src/components/notesManager/NoteType.js +++ b/viscoll-app/src/components/notesManager/NoteType.js @@ -136,6 +136,7 @@ export default class NoteType extends Component { return (
} style={{minWidth:"60px",marginLeft:"5px"}} @@ -144,10 +145,11 @@ export default class NoteType extends Component { disabled={!this.isValid(this.state.types[index])} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.onCancelUpdate(index)} + onClick={(e)=>this.onCancelUpdate(index)} />
) @@ -161,17 +163,23 @@ export default class NoteType extends Component {
this.onUpdate(e, index)}> this.onChange(v,index)} errorText={this.state.errorTypes[index]} + aria-invalid={this.state.errorTypes[index].length>0} style={{width:"75%"}} + tabIndex={this.props.tabIndex} /> { noteType==="Unknown"? "" : } {this.renderSubmitButtons(index)} @@ -185,17 +193,20 @@ export default class NoteType extends Component { } render() { - return
+ return

Add a new note type

this.onCreate(e)}>
this.onNewTypeChange(v)} errorText={this.state.errorNewType} + aria-invalid={this.state.errorNewType.length>0} style={{width: 300}} + tabIndex={this.props.tabIndex} />
@@ -204,6 +215,7 @@ export default class NoteType extends Component { primary type="submit" disabled={!this.isValid(this.state.newType)} + tabIndex={this.props.tabIndex} />
diff --git a/viscoll-app/src/components/notesManager/NotesFilter.js b/viscoll-app/src/components/notesManager/NotesFilter.js index 42e84aa1..6bb8bc71 100644 --- a/viscoll-app/src/components/notesManager/NotesFilter.js +++ b/viscoll-app/src/components/notesManager/NotesFilter.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import TextField from 'material-ui/TextField'; import Checkbox from 'material-ui/Checkbox'; +import {floatFieldLight} from '../../styles/textfield'; /** Filter notes */ class NotesFilter extends Component { @@ -17,34 +18,43 @@ class NotesFilter extends Component {
{this.setState({value});this.props.onValueChange(e,value)}} style={{marginLeft:10,marginRight:10}} value={this.state.value} + {...floatFieldLight} + tabIndex={this.props.tabIndex} />
0)?"searchOptions active":"searchOptions"}> this.props.onTypeChange("title", checked)} + tabIndex={this.props.tabIndex} /> this.props.onTypeChange("type", checked)} + tabIndex={this.props.tabIndex} /> this.props.onTypeChange("description", checked)} + tabIndex={this.props.tabIndex} />
diff --git a/viscoll-app/src/components/topbar/UserProfileForm.js b/viscoll-app/src/components/topbar/UserProfileForm.js index 95b50118..73ea3407 100644 --- a/viscoll-app/src/components/topbar/UserProfileForm.js +++ b/viscoll-app/src/components/topbar/UserProfileForm.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {floatFieldLight} from '../../styles/textfield'; import TextField from 'material-ui/TextField'; import Dialog from 'material-ui/Dialog'; import RaisedButton from 'material-ui/RaisedButton'; @@ -248,18 +249,21 @@ class UserProfileForm extends React.Component { return (
} style={{minWidth:"60px",marginLeft:"5px"}} name="submit" type="submit" disabled={type==="currentPassword"? (this.ifErrorsExist("currentPassword")||this.ifErrorsExist("newPassword")||this.ifErrorsExist("newPasswordConfirm")) : this.ifErrorsExist(type) } + onClick={() => this.handleUserUpdate(null, type)} /> } style={{minWidth:"60px",marginLeft:"5px"}} - onTouchTap={(e)=>this.handleCancelUpdate(type)} + onClick={() => this.handleCancelUpdate(type)} />
) @@ -275,7 +279,7 @@ class UserProfileForm extends React.Component { * @public */ handleUserUpdate = (event, type) => { - event.preventDefault(); + if (event) event.preventDefault(); let updatedUser = { user: { [type]: this.state[type], @@ -305,50 +309,53 @@ class UserProfileForm extends React.Component { const userProfileActions = [ this.handleUserProfileClose()} + primary + keyboardFocused={true} + onClick={() => this.handleUserProfileClose()} />, this.handleDeleteDialogToggle(true)} + labelColor="#b53c3c" + onClick={() => this.handleDeleteDialogToggle(true)} style={{float: 'left'}} /> ]; - + const deleteActions = [ this.handleDeleteDialogToggle()} + primary + keyboardFocused + onClick={() => this.handleDeleteDialogToggle()} />, this.handleUserAccountDelete()} />, ]; const unsaveActions = [ this.handleUnsavedDialogToggle()} + primary + onClick={() => this.handleUnsavedDialogToggle()} + />, this.handleUserProfileClose(true)} + primary + keyboardFocused + onClick={() => this.handleUserProfileClose(true)} />, ]; const emailConfirmActions = [ this.setState({emailMessage:false})} + primary + onClick={() => this.setState({emailMessage:false})} + keyboardFocused /> ]; @@ -359,6 +366,7 @@ class UserProfileForm extends React.Component { onChange={(e, v)=>this.onInputChange(v, "name")} name="name" floatingLabelText="Name" + floatingLabelStyle={{color:"#6e6e6e"}} errorText={this.state.errors.name} value={this.state.name} fullWidth @@ -375,6 +383,7 @@ class UserProfileForm extends React.Component { onChange={(e,v)=>this.onInputChange(v, "email")} name="email" floatingLabelText="E-mail" + floatingLabelStyle={{color:"#6e6e6e"}} errorText={this.state.errors.email} value={this.state.email} fullWidth @@ -391,6 +400,7 @@ class UserProfileForm extends React.Component { onChange={(e, v)=>this.onInputChange(v, "currentPassword")} name="currentPassword" floatingLabelText="Current Password" + {...floatFieldLight} errorText={this.state.errors.currentPassword} type="password" value={this.state.currentPassword} @@ -400,6 +410,7 @@ class UserProfileForm extends React.Component { onChange={(e, v)=>this.onInputChange(v, "newPassword")} name="newPassword" floatingLabelText="New Password" + {...floatFieldLight} errorText={this.state.errors.newPassword} type="password" value={this.state.newPassword} @@ -409,6 +420,7 @@ class UserProfileForm extends React.Component { onChange={(e, v)=>this.onInputChange(v, "newPasswordConfirm")} name="newPasswordConfirm" floatingLabelText="Confirm New Password" + {...floatFieldLight} errorText={this.state.errors.newPasswordConfirm} type="password" value={this.state.newPasswordConfirm} @@ -436,24 +448,24 @@ class UserProfileForm extends React.Component {
{emailField}
-

Update your password

+

Update your password

{password} + - {this.state.message}

; + const message = this.state.message?

{this.state.message}

: ""; let resetPassword = ""; let resetPasswordRequest = ""; @@ -113,7 +113,7 @@ class Landing extends Component {
this.toggleRegister()} label="Create account" {...btnLg} /> @@ -122,9 +122,8 @@ class Landing extends Component { let login = (
this.toggleLogin()} label="Login" {...btnLg} /> @@ -181,10 +180,10 @@ class Landing extends Component {
- Logo + Collation illustration
-
- LogoWhite +
+ VisColl logo

{message} diff --git a/viscoll-app/src/containers/CollationManager.js b/viscoll-app/src/containers/CollationManager.js index 5bde092d..2e322605 100644 --- a/viscoll-app/src/containers/CollationManager.js +++ b/viscoll-app/src/containers/CollationManager.js @@ -17,18 +17,24 @@ import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; import { connect } from "react-redux"; import { changeViewMode, handleObjectClick, + handleObjectPress, changeManagerMode, toggleFilterPanel, updateFilterSelection, - toggleTacket, + reapplyFilterProject, + toggleVisualizationDrawing, } from "../actions/editCollation/interactionActions"; import { loadProject, updateGroup, + updateNote, + deleteNote, + linkNote, + unlinkNote, } from "../actions/editCollation/modificationActions"; -import { exportProject } from "../actions/projectActions"; -import { updateProject } from "../actions/projectActions"; +import { exportProject, updateProject } from "../actions/projectActions"; import fileDownload from 'js-file-download'; +import NoteDialog from '../components/collationManager/dialog/NoteDialog'; /** Container for `TabularMode`, `VisualMode`, `InfoBox`, `TopBar`, `LoadingScreen`, and `Notification`. This container has the project sidebar embedded directly. */ @@ -51,15 +57,17 @@ class CollationManager extends Component { }, selectAll: "", leftSideBarOpen: true, - showTips: props.preferences.showTips + showTips: props.preferences.showTips, + imageViewerEnabled: false, + activeNote: null, }; } - componentWillMount() { - if (this.props.collationManager.viewMode==="VIEWING") { - this.setState({leftSideBarOpen:false}); - } - } + // componentWillMount() { + // if (this.props.collationManager.viewMode==="VIEWING") { + // this.setState({leftSideBarOpen:false}); + // } + // } componentDidMount() { if (this.props.filterPanelOpen){ @@ -81,8 +89,19 @@ class CollationManager extends Component { if (nextProps.selectedObjects.type && Object.keys(nextProps.project[nextProps.selectedObjects.type+"s"]).length===nextProps.selectedObjects.members.length) { this.setState({selectAll:nextProps.selectedObjects.type+"s"}); } - } + // Update active note + const commonNotes = this.getCommonNotes(nextProps); + const activeNoteExists = this.state.activeNote!==null && commonNotes.filter((note)=>note.id===this.state.activeNote.id).length>0; + if (!activeNoteExists) { + this.setState({activeNote:null}); + } + commonNotes.forEach((noteID)=> { + if (this.state.activeNote!==null && noteID===this.state.activeNote.id) { + this.setState({activeNote: nextProps.project.Notes[noteID]}); + } + }); + } /** * Toggle filter panel @@ -97,6 +116,9 @@ class CollationManager extends Component { this.filterHeightChange(filterPanelHeight); } + handleObjectPress = (object, event) => { + this.props.handleObjectPress(this.props.selectedObjects, object, event); + } /** * Pass the newly clicked object to the `handleObjectClick` action @@ -105,6 +127,7 @@ class CollationManager extends Component { * @public */ handleObjectClick = (object, event) => { + event.stopPropagation(); this.props.handleObjectClick( this.props.selectedObjects, object, @@ -121,8 +144,8 @@ class CollationManager extends Component { * @public */ handleViewModeChange = (value) => { - if (value==="VIEWING" && this.state.leftSideBarOpen) { - this.setState({leftSideBarOpen: false}, ()=>this.props.changeViewMode(value)); + if (value==="VIEWING") { + this.setState({leftSideBarOpen: true, imageViewerEnabled: false}, ()=>this.props.changeViewMode(value)); } else if (value!=="VIEWING" && this.state.leftSideBarOpen===false) { this.setState({leftSideBarOpen: true}, ()=>this.props.changeViewMode(value)); } else { @@ -159,7 +182,8 @@ class CollationManager extends Component { showTips: false } }; - this.props.updateProject(project, this.props.project.id) + this.props.hideProjectTip(); + this.props.updateProject(project, this.props.project.id); } handleSelection = (selection) => { @@ -191,9 +215,72 @@ class CollationManager extends Component { const filename = this.props.project.title.replace(/\s/g, "_"); fileDownload(blob, `${filename}.PNG`) }); + } + toggleImageViewer = () => { + this.setState({imageViewerEnabled: !this.state.imageViewerEnabled, leftSideBarOpen: !this.state.leftSideBarOpen}); } + closeNoteDialog = () => { + this.setState({activeNote: null}); + this.props.togglePopUp(false); + } + openNoteDialog = (note) => { + this.setState({activeNote: note}); + this.props.togglePopUp(true); + } + + /** + * Returns notes of currently selected objects + * @public + */ + getCommonNotes = (props=this.props) => { + // Find the common notes of all currently selected objects + const memberType = props.selectedObjects.type; + const members = props.selectedObjects.members; + let notes = []; + if (members.length>0) { + notes = props.project[memberType+"s"][members[0]].notes; + for (let id of members) { + notes = this.intersect(notes, props.project[memberType+"s"][id].notes); + } + } + return notes; + } + + /** + * Returns items in common + * @param {array} list1 + * @param {array} list2 + * @public + */ + 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)}); + } + + updateNote = (noteID, note) => { + this.props.updateNote(noteID, note, this.props.project.id, this.props.collationManager.filters); + } + + linkDialogNote = (noteID, objects) => { + this.props.linkNote(noteID, objects, this.props.project.id, this.props.collationManager.filters); + } + + linkAndUnlinkNotes = (noteID, linkObjects, unlinkObjects) => { + this.props.linkAndUnlinkNotes(noteID, linkObjects, unlinkObjects, this.props.project.id, this.props.collationManager.filters); + } + + unlinkDialogNote = (noteID, objects) => { + this.props.unlinkNote(noteID, objects, this.props.project.id, this.props.collationManager.filters); + } + + deleteNote = (noteID) => { + this.props.deleteNote(noteID, this.props.project.id, this.props.collationManager.filters); + // this.setState({activeNote:null}); + } render() { const containerStyle = {top: 85, right: "2%", height: 'inherit', maxHeight: '80%', width: '28%'}; @@ -207,15 +294,20 @@ class CollationManager extends Component { filterOpen={this.props.filterPanelOpen} viewMode={this.props.collationManager.viewMode} history={this.props.history} + showImageViewerButton={this.props.collationManager.viewMode==="VIEWING"} + imageViewerEnabled={this.state.imageViewerEnabled} + toggleImageViewer={this.toggleImageViewer} + tabIndex={this.props.popUpActive?-1:0} + togglePopUp={this.props.togglePopUp} > - - - + + + ); @@ -225,13 +317,15 @@ class CollationManager extends Component { const tip = this.props.selectedObjects.members.length>1 ? batchEditTip : singleEditTip let tipsDiv; if (this.props.managerMode==="collationManager" && this.props.preferences.showTips===true) { - tipsDiv = + tipsDiv =
@@ -251,28 +345,36 @@ class CollationManager extends Component { > + value="Rectos" + aria-label="Select All Rectos" + label="Select All Rectos" + labelStyle={{color:"#ffffff",fontSize:"0.9em"}} + iconStyle={{fill:"#4ED6CB"}} + tabIndex={this.props.popUpActive?-1:0} + /> ); @@ -290,79 +392,74 @@ class CollationManager extends Component { ); const sidebar = ( -
-
+
+
{tipsDiv} - -
this.props.changeManagerMode("collationManager")} > - Collation -
-
this.props.changeManagerMode("notesManager")} > - Notes -
-
this.props.changeManagerMode("imageManager")} > - Images -
-
- + { this.props.collationManager.viewMode !== "VIEWING"? + + + + : "" } + {selectionRadioGroup} this.setState({selectAll:""},this.handleSelection(""))} secondary fullWidth style={this.state.selectAll===""?{display:"none"}:{}} + tabIndex={this.props.popUpActive?-1:0} /> - -

Export Collation Data

+ +

Export Collation Data

-
- this.handleExportToggle(true, "json", "JSON")} - /> -
-
- this.handleExportToggle(true, "xml", "XML")} - /> -
-
- this.handleExportToggle(true, "formula", "Collation Formula")} - /> -
+ this.handleExportToggle(true, "json", "JSON")} + tabIndex={this.props.popUpActive?-1:0} + /> + this.handleExportToggle(true, "xml", "XML")} + tabIndex={this.props.popUpActive?-1:0} + />
-

Export Collation Diagram

+

Export Collation Diagram

-
- -
+
@@ -374,7 +471,16 @@ class CollationManager extends Component { className="infoBox" style={{...this.state.contentStyle, ...this.state.infoBoxStyle}} > - +
) @@ -382,12 +488,15 @@ class CollationManager extends Component { if (this.props.project.groupIDs.length>0){ if (this.props.collationManager.viewMode==="TABULAR") { workspace = ( -
+
{infobox} @@ -396,15 +505,18 @@ class CollationManager extends Component { ); } else if (this.props.collationManager.viewMode==="VISUAL") { workspace = ( -
+
{infobox} @@ -413,13 +525,14 @@ class CollationManager extends Component { ); } else { workspace = ( -
-
+
+
{infobox} @@ -430,7 +543,7 @@ class CollationManager extends Component { } if (this.props.project.groupIDs.length===0 && !this.props.loading){ workspace = ( -
+

It looks like you have an empty project. Add a new Group to start collating. @@ -447,9 +560,31 @@ class CollationManager extends Component { {workspace} +

); } @@ -487,16 +622,11 @@ const mapStateToProps = (state) => { collationManager: state.active.collationManager, loading: state.global.loading, exportedData: state.active.exportedData, - tacketing: state.active.collationManager.visualizations.tacketing, }; }; const mapDispatchToProps = (dispatch) => { return { - toggleTacket: (toggle) => { - dispatch(toggleTacket(toggle)); - }, - loadProject: (projectID, props) => { dispatch(loadProject(projectID)); }, @@ -505,6 +635,10 @@ const mapDispatchToProps = (dispatch) => { dispatch(changeViewMode(viewMode)); }, + handleObjectPress: (selectedObjects, object, event) => { + dispatch(handleObjectPress(selectedObjects, object, event)); + }, + handleObjectClick: (selectedObjects, object, event, Groups, Leafs, Rectos, Versos) => { dispatch(handleObjectClick(selectedObjects, object, event, {Groups, Leafs, Rectos, Versos})); }, @@ -521,10 +655,53 @@ const mapDispatchToProps = (dispatch) => { dispatch(updateProject(projectID, project)); }, + hideProjectTip: () => { + dispatch({type: "HIDE_PROJECT_TIP"}); + }, + updateGroup: (groupID, group, props) => { dispatch(updateGroup(groupID, group)); }, + updateNote: (noteID, note, projectID, filters) => { + dispatch(updateNote(noteID, note)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + deleteNote: (noteID, projectID, filters) => { + dispatch(deleteNote(noteID)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + linkNote: (noteID, object, projectID, filters) => { + dispatch(linkNote(noteID, object)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + unlinkNote: (noteID, object, projectID, filters) => { + dispatch(unlinkNote(noteID, object)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + }, + + linkAndUnlinkNotes: (noteID, linkObjects, unlinkObjects, projectID, filters) => { + if (linkObjects.length > 0 && unlinkObjects.length > 0){ + dispatch(linkNote(noteID, linkObjects)) + .then(action=>dispatch(unlinkNote(noteID, unlinkObjects))) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + else if (linkObjects.length > 0) { + dispatch(linkNote(noteID, linkObjects)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + else if (unlinkObjects.length > 0) { + dispatch(unlinkNote(noteID, unlinkObjects)) + .then(()=>dispatch(reapplyFilterProject(projectID, filters))); + } + }, + toggleVisualizationDrawing: (data) => { + dispatch(toggleVisualizationDrawing(data)); + }, + updateFilterSelection: ( selection, Groups, diff --git a/viscoll-app/src/containers/Dashboard.js b/viscoll-app/src/containers/Dashboard.js index 1a4ce6f3..55dbeb01 100644 --- a/viscoll-app/src/containers/Dashboard.js +++ b/viscoll-app/src/containers/Dashboard.js @@ -33,6 +33,8 @@ class Dashboard extends Component { selectedProjectIndex: -1, selectedProject: {}, projectDrawerOpen: false, + userProfileDialogOpen: false, + feedbackOpen: false, }; } @@ -97,17 +99,27 @@ class Dashboard extends Component { }); } + modalIsOpen = () => { + return this.state.feedbackOpen || this.state.newProjectModalOpen || this.state.newProjectPopoverOpen || this.state.userProfileDialogOpen; + } + + userProfileToggle = (userProfileDialogOpen) => { + this.setState({userProfileDialogOpen}); + } + render() { let sidebar = ( -
+


this.handleNewProjectModalToggle(true)} + onClick={() => this.handleNewProjectModalToggle(true)} + tabIndex={this.modalIsOpen()? -1 : 0} + aria-label="Create new collation" />
); @@ -129,15 +141,16 @@ class Dashboard extends Component { deleteProject={this.props.deleteProject} user={this.props.user} history={this.props.history} + tabIndex={this.modalIsOpen()? -1 : 0} /> ); return (
- + - + {sidebar} @@ -152,17 +165,18 @@ class Dashboard extends Component { importStatus={this.props.importStatus} cloneProject={this.handleCloneProject} /> -
+
- + this.setState({feedbackOpen:v})}/>
); } diff --git a/viscoll-app/src/containers/Feedback.js b/viscoll-app/src/containers/Feedback.js index dea829b4..b86dc6b7 100644 --- a/viscoll-app/src/containers/Feedback.js +++ b/viscoll-app/src/containers/Feedback.js @@ -1,7 +1,8 @@ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import { connect } from "react-redux"; import Dialog from 'material-ui/Dialog'; import FlatButton from 'material-ui/FlatButton'; +import RaisedButton from 'material-ui/RaisedButton'; import TextField from 'material-ui/TextField'; import ClientJS from 'clientjs'; import { exportProjectBeforeFeedback } from "../actions/projectActions"; @@ -18,7 +19,7 @@ class Feedback extends Component { } } handleOpen = () => { - this.setState({open: true}); + this.setState({ open: true }); } handleClose = () => { this.setState({ @@ -26,18 +27,20 @@ class Feedback extends Component { title: "", feedback: "", }); + this.props.togglePopUp(false); } onChange = (type, value) => { - this.setState({[type]:value}); + this.setState({ [type]: value }); } submit = () => { - let feedback = "Feedback Message:\n" + this.state.feedback + "\n\n"; - try{ + let feedback = this.state.feedback; + let browserInformation; + try { const client = new ClientJS(); const result = client.getResult(); - feedback = feedback + "Browser Information:\n" + JSON.stringify(result); - } catch (e){} - this.props.sendFeedback(this.state.title, feedback, this.props.userID, this.props.projectID); + browserInformation = JSON.stringify(result); + } catch (e) { } + this.props.sendFeedback(this.state.title, feedback, browserInformation, this.props.projectID); this.handleClose(); } render() { @@ -45,23 +48,24 @@ class Feedback extends Component { this.handleClose()} />, - this.submit()} />, ]; return (
- { this.handleOpen(); this.props.togglePopUp(true) }} backgroundColor="rgba(82, 108, 145, 0.2)" + tabIndex={this.props.tabIndex} />

Bug? Suggestions? Let us know!

-
-

Title

+
+ Title
this.onChange("title", v)} + onChange={(e, v) => this.onChange("title", v)} + autoFocus />
-
-

Feedback

+
+ Feedback
- this.onChange("feedback", v)} + onChange={(e) => this.onChange("feedback", e.target.value)} + rows={5} />
@@ -114,17 +119,17 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { - sendFeedback: (title, message, userID, projectID) => { - if (projectID){ + sendFeedback: (title, message, browserInformation, projectID) => { + if (projectID) { dispatch(exportProjectBeforeFeedback(projectID, "json")) - .then((action)=>{ - if (action.type==="EXPORT_SUCCESS"){ - message = message + "\n\nProject Information:\n" + JSON.stringify(action.payload); - dispatch(sendFeedback(title, message, userID)); - } - }) + .then((action) => { + if (action.type === "EXPORT_SUCCESS") { + const project = JSON.stringify(action.payload); + dispatch(sendFeedback(title, message, browserInformation, project)); + } + }) } else { - dispatch(sendFeedback(title, message, userID)); + dispatch(sendFeedback(title, message, browserInformation)); } } }; diff --git a/viscoll-app/src/containers/Filter.js b/viscoll-app/src/containers/Filter.js index 074fcdcc..69c7e0ef 100644 --- a/viscoll-app/src/containers/Filter.js +++ b/viscoll-app/src/containers/Filter.js @@ -334,13 +334,14 @@ class Filter extends Component { addRow={this.addRow} disableAddRow={this.disableAddRow()} disableNewRow={this.disableNewRow()} + tabIndex={this.props.tabIndex} /> ); } let filterContainerStyle = this.props.open?{top:'56px'}:{top:'-'+this.state.filterPanelHeight+'px'}; if (this.props.fullWidth) filterContainerStyle.width="100%"; let panel = -
+
{queries}
@@ -351,16 +352,18 @@ class Filter extends Component {
} + tabIndex={this.props.tabIndex} + style={this.props.open?{}:{display:"none"}} /> {this.setState({select:v});this.handleSelection(v)}} > - - - - + 0?false:true} /> + 0?false:true}/> + 0?false:true}/> + 0?false:true}/> {this.props.selectedObjects.members.length > 0 ? : null @@ -397,17 +402,17 @@ class Filter extends Component { this.resetFilters()} - style={{marginRight: 15}} - backgroundColor="#D87979" - labelColor="#ffffff" + backgroundColor="#b53c3c" + labelColor="#ffffff" + tabIndex={this.props.tabIndex} + style={this.props.open?{marginRight: 15}:{display:"none"}} />
- - return panel; + return panel; } } diff --git a/viscoll-app/src/containers/ImageManager.js b/viscoll-app/src/containers/ImageManager.js index ecd2f5c1..36da8a76 100644 --- a/viscoll-app/src/containers/ImageManager.js +++ b/viscoll-app/src/containers/ImageManager.js @@ -18,9 +18,19 @@ import { mapSidesToImages } from "../actions/editCollation/modificationActions"; import { sendFeedback } from "../actions/userActions"; import ManageManifests from '../components/imageManager/ManageManifests'; import MapImages from '../components/imageManager/MapImages'; +import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton'; +import FlatButton from 'material-ui/FlatButton'; + class ImageManager extends Component { + constructor(props) { + super(props); + this.state = { + selectAll: "" + }; + } + createManifest = (manifest) => { this.props.createManifest(this.props.projectID, manifest) } @@ -33,6 +43,10 @@ class ImageManager extends Component { this.props.deleteManifest(this.props.projectID, manifest) } + handleSelection = (selectAll) => { + this.setState({ selectAll }) + } + render() { let content = ""; if (this.props.activeTab==="MANAGE") { @@ -44,65 +58,142 @@ class ImageManager extends Component { deleteManifest={this.deleteManifest} createManifestError={this.props.createManifestError} cancelCreateManifest={this.props.cancelCreateManifest} + tabIndex={this.props.popUpActive?-1:0} + togglePopUp={this.props.togglePopUp} /> ) } else { content = ( ) } + let selectionRadioGroup = ( + this.setState({selectAll: v})} + > + + + + + + ); + + const sidebar = ( -
+

- -
+
-
+
-
+
+
+ {this.props.activeTab==="MAP" ? + + {selectionRadioGroup} + this.setState({selectAll:""})} + secondary + fullWidth + style={this.state.selectAll===""?{display:"none"}:{}} + tabIndex={this.props.popUpActive?-1:0} + /> + + : null + }
); - return
-
- - this.props.changeImageTab(v)} + return ( +
+
+ - - - - - {sidebar} -
- {content} + this.props.changeImageTab(v)} + > + + + + + {sidebar} +
+ {content} +
-
+ ); } } @@ -113,6 +204,9 @@ const mapStateToProps = (state) => { Leafs: state.active.project.Leafs, Rectos: state.active.project.Rectos, Versos: state.active.project.Versos, + leafIDs: state.active.project.leafIDs, + rectoIDs: state.active.project.rectoIDs, + versoIDs: state.active.project.versoIDs, activeTab: state.active.imageManager.activeTab, managerMode: state.active.managerMode, createManifestError: state.active.imageManager.manageSources.error @@ -141,8 +235,8 @@ const mapDispatchToProps = (dispatch) => { cancelCreateManifest: () => { dispatch(cancelCreateManifest()) }, - mapSidesToImages: (linkedSideIDs, images, unlinkedSideIDs) => { - dispatch(mapSidesToImages(linkedSideIDs, images, unlinkedSideIDs)) + mapSidesToImages: (sideMappings) => { + dispatch(mapSidesToImages(sideMappings)) }, }; }; diff --git a/viscoll-app/src/containers/InfoBox.js b/viscoll-app/src/containers/InfoBox.js index 5b9d8a17..1f30892c 100644 --- a/viscoll-app/src/containers/InfoBox.js +++ b/viscoll-app/src/containers/InfoBox.js @@ -23,10 +23,7 @@ import { updateSide, updateSides, addNote, - updateNote, - deleteNote, linkNote, - unlinkNote, } from '../actions/editCollation/modificationActions'; import { toggleVisibility, @@ -34,7 +31,7 @@ import { flashGroups, changeInfoBoxTab, reapplyFilterProject, - toggleTacket, + toggleVisualizationDrawing, } from '../actions/editCollation/interactionActions'; @@ -150,42 +147,8 @@ class InfoBox extends React.Component { */ updateSides = (sides) => { this.props.updateSides(sides, this.props.projectID, this.props.filters); } - /** - * Returns items in common - * @param {array} list1 - * @param {array} list2 - * @public - */ - 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)}); - } - - /** - * Returns notes of currently selected objects - * @public - */ - getCommonNotes = () => { - // Find the common notes of all currently selected objects - const memberType = this.props.selectedObjects.type; - const members = this.props.selectedObjects.members; - let notes = this.props[memberType+"s"][members[0]].notes; - for (let id of members) { - notes = this.intersect(notes, this.props[memberType+"s"][id].notes); - } - return notes; - } - updateNote = (noteID, note) => { - this.props.updateNote(noteID, note, this.props.projectID, this.props.filters); - } - linkDialogNote = (noteID, objects) => { - this.props.linkNote(noteID, objects, this.props.projectID, this.props.filters); - } - linkNote = (noteID) => { let objects = []; let type = this.props.selectedObjects.type; @@ -194,29 +157,19 @@ class InfoBox extends React.Component { for (let id of this.props.selectedObjects.members) { objects.push({id, type}); } - this.props.linkNote(noteID, objects, this.props.projectID, this.props.filters); - } - - linkAndUnlinkNotes = (noteID, linkObjects, unlinkObjects) => { - this.props.linkAndUnlinkNotes(noteID, linkObjects, unlinkObjects, this.props.projectID, this.props.filters); - } - - unlinkDialogNote = (noteID, objects) => { - this.props.unlinkNote(noteID, objects, this.props.projectID, this.props.filters); + this.props.action.linkNote(noteID, objects, this.props.projectID, this.props.filters); } unlinkNote = (noteID, sideIndex) => { let objects = []; let type = this.props.selectedObjects.type; - if (type==="Recto" || type==="Verso") - type = "Side"; for (let id of this.props.selectedObjects.members) { objects.push({id, type}); } - this.props.unlinkNote(noteID, objects, this.props.projectID, this.props.filters); + this.props.action.unlinkNote(noteID, objects, this.props.projectID, this.props.filters); } - createAndAttachNote = (noteTitle, noteType, description) => { + createAndAttachNote = (noteTitle, noteType, description, show) => { let objects = []; let type = this.props.selectedObjects.type; if (type==="Recto" || type==="Verso") @@ -229,15 +182,11 @@ class InfoBox extends React.Component { title: noteTitle, type: noteType, description: description, + show: show } this.props.createAndAttachNote(note, objects, this.props.projectID, this.props.filters); } - deleteNote = (noteID) => { - this.props.deleteNote(noteID, this.props.projectID, this.props.filters) - } - - handleChangeInfoBoxTab = (value, event) => { this.props.changeInfoBoxTab( value, @@ -267,7 +216,7 @@ class InfoBox extends React.Component { primary label={this.props.selectedGroups ? "Add" : "Add New Group"} style={this.props.selectedGroups ? {width:"49%", float:"left", marginRight:"2%"} : {width:"100%", float:"left", marginRight:"2%"}} - onTouchTap={()=>this.toggleAddGroupDialog(true)} + onClick={()=>this.toggleAddGroupDialog(true)} /> - - + +
); const groupTab = ( - + ); const noteActions = { - updateNote: this.updateNote, - deleteNote: this.deleteNote, linkNote: this.linkNote, unlinkNote: this.unlinkNote, - linkAndUnlinkNotes: this.linkAndUnlinkNotes, - linkDialogNote: this.linkDialogNote, - unlinkDialogNote: this.unlinkDialogNote, createAndAttachNote: this.createAndAttachNote } if (this.props.selectedObjects.type === "Group") { return ( -
+
{groupTab}
); } else if (this.props.selectedObjects.type === "Leaf") { return ( -
+
{leafSideTabs}
); } else if (this.props.selectedObjects.type === "Recto") { return ( -
+
{leafSideTabs}
); } else if (this.props.selectedObjects.type === "Verso") { return ( -
+
{leafSideTabs}
); @@ -465,14 +435,14 @@ const mapStateToProps = (state) => { collationManager: state.active.collationManager, notesManager: state.active.notesManager, filters: state.active.collationManager.filters, - tacketing: state.active.collationManager.visualizations.tacketing, + tacketed: state.active.collationManager.visualizations.tacketed, }; }; const mapDispatchToProps = (dispatch) => { return { - toggleTacket: (toggle) => { - dispatch(toggleTacket(toggle)); + toggleVisualizationDrawing: (data) => { + dispatch(toggleVisualizationDrawing(data)); }, addLeafs: (leaf, additional, projectID, filters) => { @@ -556,16 +526,6 @@ const mapDispatchToProps = (dispatch) => { dispatch(changeInfoBoxTab(newType, selectedObjects, {Leafs, Rectos, Versos})); }, - updateNote: (noteID, note, projectID, filters) => { - dispatch(updateNote(noteID, note)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - }, - - deleteNote: (noteID, projectID, filters) => { - dispatch(deleteNote(noteID)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - }, - createAndAttachNote: (note, objects, projectID, filters) => { dispatch(addNote(note)) .then((action)=> { @@ -580,32 +540,6 @@ const mapDispatchToProps = (dispatch) => { }) .then(()=>dispatch(reapplyFilterProject(projectID, filters))); }, - - linkNote: (noteID, object, projectID, filters) => { - dispatch(linkNote(noteID, object)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - }, - - unlinkNote: (noteID, object, projectID, filters) => { - dispatch(unlinkNote(noteID, object)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - }, - - linkAndUnlinkNotes: (noteID, linkObjects, unlinkObjects, projectID, filters) => { - if (linkObjects.length > 0 && unlinkObjects.length > 0){ - dispatch(linkNote(noteID, linkObjects)) - .then(action=>dispatch(unlinkNote(noteID, unlinkObjects))) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - } - else if (linkObjects.length > 0) { - dispatch(linkNote(noteID, linkObjects)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - } - else if (unlinkObjects.length > 0) { - dispatch(unlinkNote(noteID, unlinkObjects)) - .then(()=>dispatch(reapplyFilterProject(projectID, filters))); - } - } }; }; diff --git a/viscoll-app/src/containers/NotesManager.js b/viscoll-app/src/containers/NotesManager.js index 6a788028..dd54a4c8 100644 --- a/viscoll-app/src/containers/NotesManager.js +++ b/viscoll-app/src/containers/NotesManager.js @@ -2,7 +2,6 @@ import React, {Component} from 'react'; import { connect } from "react-redux"; import PropTypes from 'prop-types'; import TopBar from "./TopBar"; -import Feedback from "./Feedback"; import ManageNotes from "../components/notesManager/ManageNotes"; import NoteType from "../components/notesManager/NoteType"; import {Tabs, Tab} from 'material-ui/Tabs'; @@ -130,6 +129,8 @@ class NotesManager extends Component { Leafs={this.props.Leafs} Rectos={this.props.Rectos} Versos={this.props.Versos} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.popUpActive?-1:0} /> } else if (this.props.activeTab==="TYPES") { content = this.props.deleteNoteType(noteTypes, this.props) }} + togglePopUp={this.props.togglePopUp} + tabIndex={this.props.popUpActive?-1:0} /> } const sidebar = ( -
+

- -
+
-
+
-
this.props.changeManagerMode("imageManager")} > - Images -
+
); @@ -175,23 +179,21 @@ class NotesManager extends Component { onTypeChange={this.onTypeChange} filterTypes={this.state.filterTypes} history={this.props.history} + tabIndex={this.props.popUpActive?-1:0} > this.props.changeNotesTab(v)} > - - + + {sidebar}
{content}
- this.props.sendFeedback(title, message, this.props.user.id)}} - />
) } diff --git a/viscoll-app/src/containers/Project.js b/viscoll-app/src/containers/Project.js index 8e6e0073..b95c5483 100644 --- a/viscoll-app/src/containers/Project.js +++ b/viscoll-app/src/containers/Project.js @@ -13,6 +13,13 @@ import { loadProject } from "../actions/editCollation/modificationActions"; /** Container for 'Manager (Collation or Notes or Image)', `LoadingScreen`, and `Notification`. */ class Project extends Component { + constructor(props) { + super(props); + this.state = { + popUpActive: false, + }; + } + componentWillMount() { const projectID = this.props.location.pathname.split("/")[2]; this.props.user.authenticated ? this.props.loadProject(projectID) : this.props.history.push('/'); @@ -22,10 +29,14 @@ class Project extends Component { if (!this.props.user.authenticated) this.props.history.push('/'); } + togglePopUp = (value) => { + this.setState({popUpActive: value}); + } + render() { - const collationManager = (); - const notesManager = (); - const imageManager = (); + const collationManager = (); + const notesManager = (); + const imageManager = (); let manager; switch (this.props.managerMode) { case "collationManager": @@ -46,7 +57,7 @@ class Project extends Component { {manager} - +
) } diff --git a/viscoll-app/src/containers/TopBar.js b/viscoll-app/src/containers/TopBar.js index 8bf739bc..9f2096e4 100644 --- a/viscoll-app/src/containers/TopBar.js +++ b/viscoll-app/src/containers/TopBar.js @@ -10,6 +10,7 @@ import UserProfileForm from '../components/topbar/UserProfileForm'; import FlatButton from 'material-ui/FlatButton'; import NotesFilter from "../components/notesManager/NotesFilter"; import FilterIcon from 'material-ui/svg-icons/content/filter-list'; +import Image from 'material-ui/svg-icons/image/image'; import imgLogo from '../assets/logo_white.svg'; import { connect } from "react-redux"; @@ -46,6 +47,7 @@ class TopBar extends Component { */ toggleUserProfile = (userProfileModalOpen=false) => { this.setState({ userProfileModalOpen }); + this.props.togglePopUp(userProfileModalOpen); } /** @@ -79,34 +81,63 @@ class TopBar extends Component { if (this.props.user.name) { UserMenu = ( {this.props.user.name.charAt(0).toUpperCase()} } + iconButtonElement={ + + {this.props.user.name.charAt(0).toUpperCase()} + } targetOrigin={{horizontal: 'right', vertical: 'top'}} anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} > - this.toggleUserProfile(true)} /> - + this.toggleUserProfile(true)} + /> + this.handleUserLogout()} + /> ); } return ( -
-
- Logo -
+
+ {this.props.children} + {this.props.showImageViewerButton ? + this.props.toggleImageViewer()} + icon={} + style={{marginRight: 5}} + tabIndex={this.props.tabIndex} + /> + : null + } {this.props.toggleFilterDrawer? {e.preventDefault();this.props.toggleFilterDrawer()}} + label={this.props.filterOpen? "Hide filter" : "Filter"} icon={} - > - + style={{marginLeft:0}} + tabIndex={this.props.tabIndex} + /> : null } {this.props.notesFilter ? : null} + {UserMenu} diff --git a/viscoll-app/src/reducers/editCollationReducer.js b/viscoll-app/src/reducers/editCollationReducer.js index 460ab96e..2af36df3 100644 --- a/viscoll-app/src/reducers/editCollationReducer.js +++ b/viscoll-app/src/reducers/editCollationReducer.js @@ -60,15 +60,17 @@ export default function editCollationReducer(state=initialState, action) { } }; break; - case "TOGGLE_TACKET": + case "TOGGLE_VISUALIZATION_DRAWING": + let visualizations = { + ...state.collationManager.visualizations, + }; + visualizations[action.payload.type] = action.payload.value; + state = { ...state, collationManager: { ...state.collationManager, - visualizations: { - ...state.collationManager.visualizations, - tacketing: action.payload, - } + visualizations, } } break; @@ -129,7 +131,18 @@ export default function editCollationReducer(state=initialState, action) { case "DELETE_MANIFEST_FAILED": case "MAP_SIDES_FAILED": break; - + case "HIDE_PROJECT_TIP": + state = { + ...state, + project: { + ...state.project, + preferences: { + ...state.project.preferences, + showTips: false + } + } + } + break; // INTERACTIONS case "persist/REHYDRATE": state = {...state, ...action.payload.active} @@ -307,7 +320,6 @@ export default function editCollationReducer(state=initialState, action) { break; case "EXPORT_SUCCESS": let exportedData = action.payload; - console.log() if (action.payload.type==="xml"){ exportedData = action.payload.data; } else if (action.payload.type==="formula") { diff --git a/viscoll-app/src/reducers/initialStates/active.js b/viscoll-app/src/reducers/initialStates/active.js index b217d58d..15f59488 100644 --- a/viscoll-app/src/reducers/initialStates/active.js +++ b/viscoll-app/src/reducers/initialStates/active.js @@ -56,7 +56,7 @@ export const initialState = { { name: 'type', displayName: 'Type', - options: ['None', 'Original', 'Added', 'Missing', 'Hook', 'Flyleaf', 'Endleaf', 'Replaced'], + options: ['None', 'Original', 'Added', 'Missing', 'Hook', 'Endleaf', 'Replaced'], isDropdown: true, }, { @@ -73,13 +73,13 @@ export const initialState = { { name: 'attached_above', displayName: 'Attached Above', - options: ['None', 'Glued', 'Other'], + options: ['None', 'Glued (Partial)', 'Glued (Complete)', 'Glued (Drumming)', 'Other'], isDropdown: true, }, { name: 'attached_below', displayName: 'Attached Below', - options: ['None', 'Glued', 'Other'], + options: ['None', 'Glued (Partial)', 'Glued (Complete)', 'Glued (Drumming)', 'Other'], isDropdown: true, }, { @@ -171,7 +171,8 @@ export const initialState = { groups: [] }, visualizations: { - tacketing: "", + tacketed: "", + sewing: "", } }, notesManager: { diff --git a/viscoll-app/src/styles/App.css b/viscoll-app/src/styles/App.css index 4e7c3b3c..1517dc5c 100644 --- a/viscoll-app/src/styles/App.css +++ b/viscoll-app/src/styles/App.css @@ -41,12 +41,13 @@ display: block; top: 55px; width: 18%; - height: 100%; + height: 99%; background: #3A4B55; opacity: 1; -webkit-transition: all 200ms ease-in-out; -ms-transition: all 200ms ease-in-out; - transition: all 200ms ease-in-out; } + transition: all 200ms ease-in-out; + overflow-y: auto; } .sidebar.hidden { opacity: 0; } .sidebar hr { @@ -55,7 +56,25 @@ .sidebar h1 { color: #FFFFFF; font-size: 1em; - font-weight: lighter; } + font-weight: 600; } + .sidebar h2 { + color: #FFFFFF; + font-size: 0.8em; + font-weight: lighter; + padding: 1em 0em; + margin: 0em; + text-transform: uppercase; } + .sidebar h2:nth-child(1) { + padding-top: 0em; } + .sidebar .panel .header { + padding: 0.5em 1em; + display: flex; + justify-content: space-between; } + .sidebar .panel .content { + padding: 1em; + background: rgba(82, 108, 145, 0.2); } + .sidebar .panel .content.hidden { + display: none; } .sidebar .selectMode { padding: 1em 1em 2em 1em; text-align: center; @@ -76,21 +95,25 @@ .sidebar .manager { text-align: center; padding: 0.5em 0em; - cursor: pointer; - margin: 0px -16px; + margin: 0px; text-transform: uppercase; -webkit-transition: all 100ms ease-in-out; -ms-transition: all 100ms ease-in-out; - transition: all 100ms ease-in-out; } + transition: all 100ms ease-in-out; + width: 100%; + border: 0; + background: none; + color: #FFFFFF; + font-size: 1em; } .sidebar .manager:hover { - background: rgba(255, 255, 255, 0.02); + background: rgba(78, 214, 203, 0.1); font-weight: bold; } .sidebar .manager.active { - background: rgba(255, 255, 255, 0.05); + background: #4ED6CB; font-weight: bold; - border-left: 3px solid #4ED6CB; } - .sidebar .export div + div { - margin-top: 10px; } + color: #4e4e4e; } + .sidebar .export { + line-height: 45px; } .feedback { position: fixed; @@ -142,6 +165,16 @@ width: 35%; } .infoBox .inner .input { width: 55%; } + .infoBox button.image { + border: 0; + background: none; + padding: 0; + margin: 0; + font-size: 1em; + overflow: none; + max-width: 40%; } + .infoBox button.image + button { + margin-left: 0.5em; } .workspace, .projectWorkspace, .dashboardWorkspace, .notesWorkspace, .imageWorkspace { position: absolute; @@ -170,32 +203,58 @@ .notesWorkspace, .imageWorkspace { width: 82%; } +.groupContainer { + -webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); + box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); + -webkit-transition: border 150ms ease-in-out; + -ms-transition: border 150ms ease-in-out; + transition: border 150ms ease-in-out; + background: #FFFFFF; + margin-bottom: 1em; + cursor: pointer; + border: 2px solid #FFFFFF; } + .groupContainer input { + opacity: 0; + width: 1px; + height: 1px; + margin-top: -10px; + float: right; } + .groupContainer.focus { + border: 2px solid #4ED6CB; } + .groupContainer.active { + background: #4ED6CB; } + .groupContainer .groupMembers { + padding: 0em 1em 1em 1em; } + .groupContainer .groupMembers.hidden { + display: none; } + .itemContainer { display: flex; align-items: stretch; } .itemContainer.group { - margin-top: -21px; - -webkit-transition: background 100ms ease-in-out; - -ms-transition: background 100ms ease-in-out; - transition: background 100ms ease-in-out; } - .itemContainer.group:hover { - background: #caf3ef !important; - cursor: pointer; } - .itemContainer.group.active:hover { - background: #4ED6CB !important; } + justify-content: space-between; } + .itemContainer.group .groupSection { + display: flex; } + .itemContainer.group .toggleButton { + float: right; } -.leafSection { - display: flex; - flex-grow: 1; - border: 1px solid #FFFFFF; - -webkit-transition: background 100ms ease-in-out; - -ms-transition: background 100ms ease-in-out; - transition: background 100ms ease-in-out; } - .leafSection:hover { - background: #caf3ef; - cursor: pointer; } - .leafSection.active:hover { - background: #4ED6CB !important; } +.leafContainer { + -webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); + box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1); + margin-bottom: 0.2em; + cursor: pointer; + background: #FFFFFF; } + .leafContainer .leafSection { + display: flex; + flex-grow: 1; + -webkit-transition: border 100ms ease-in-out; + -ms-transition: border 100ms ease-in-out; + transition: border 100ms ease-in-out; + border: 2px solid #FFFFFF; } + .leafContainer .leafSection.active { + background: #4ED6CB; } + .leafContainer .leafSection.focus { + border: 2px solid #4ED6CB; } .itemName { width: 70px; @@ -239,15 +298,19 @@ flex-grow: 1; display: flex; flex-direction: column; - border-left: 1px solid rgba(78, 78, 78, 0.15); } + border-left: 1px solid rgba(78, 78, 78, 0.15); + overflow: hidden; } .sideSection .side { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - border: 1px solid #FFFFFF; } - .sideSection .side:hover { - background: #caf3ef; - cursor: pointer; } + border: 2px solid #FFFFFF; + cursor: pointer; } + .sideSection .side.active { + background: #4ED6CB; } + .sideSection .side.focus { + border: 2px solid #4ED6CB; + color: #4e4e4e; } .sideSection .side .name { width: 40px; display: inline-block; @@ -274,24 +337,29 @@ .sideSection .side .attribute.active { color: #4e4e4e; } .sideSection .side:first-child { - border-bottom: 1px solid rgba(78, 78, 78, 0.15); } + border-bottom: 2px solid rgba(78, 78, 78, 0.1); } + .sideSection .side:first-child.focus { + border-bottom: 2px solid #4ED6CB; } .sideToggle { width: 20px; - height: 44px; - border-left: 1px solid rgba(78, 78, 78, 0.15); } + height: 49px; + border-left: 1px solid rgba(78, 78, 78, 0.15); + cursor: pointer; } .sideToggle .side { display: block; - width: 20px; - height: 20px; - padding-top: 2px; - padding-left: 5px; - color: rgba(78, 78, 78, 0.6); } + width: 16px; + height: 18px; + padding-top: 3px; + text-align: center; + color: rgba(78, 78, 78, 0.6); + border: 2px solid #FFFFFF; } .sideToggle .side:first-child { border-bottom: 1px solid rgba(78, 78, 78, 0.15); } - .sideToggle .side:hover { - background: #a1e9e3; - cursor: pointer; + .sideToggle .side.active { + background: #4ED6CB; } + .sideToggle .side.focus { + border: 2px solid #4ED6CB; color: #4e4e4e; } .flash { @@ -363,26 +431,14 @@ padding: 1em 2em 0em 2em; } .notesManager .browse { height: 100%; } - .notesManager .browse .notesList .item { - margin: 1em; - padding: 1em; - display: block; - background: #F2F2F2; - cursor: pointer; - -webkit-transition: background 200ms ease-in-out; - -ms-transition: background 200ms ease-in-out; - transition: background 200ms ease-in-out; } - .notesManager .browse .notesList .item:hover { - background: #fcfcfc; } - .notesManager .browse .notesList .item.active { - font-weight: 600; - background: #4ED6CB; } - .notesManager .browse .notesList .item.add { - border: 1px solid #93dca6; - background: white; } - .notesManager .browse .notesList .item.add.active { - background: #34A251; - color: #FFFFFF; } + .notesManager .browse .notesList { + text-align: center; } + .notesManager .browse .notesList .item { + margin: 0.5em 0em 0em 0em; + overflow: hidden; + -webkit-transition: background 200ms ease-in-out; + -ms-transition: background 200ms ease-in-out; + transition: background 200ms ease-in-out; } .notesManager .browse .details { position: relative; left: 256px; @@ -534,6 +590,42 @@ color: #FFFFFF; margin-bottom: 30px; } +#listView .header { + display: flex; + padding: 1em 2em; + font-size: 0.8em; } + #listView .header div { + width: 50%; } +#listView button { + width: 100%; + display: flex; + text-align: left; + border-left: 1px solid rgba(255, 255, 255, 0.5); + border-right: 1px solid rgba(255, 255, 255, 0.5); + border-top: 1px solid rgba(114, 114, 114, 0.2); + border-bottom: 1px solid rgba(255, 255, 255, 0.5); + padding: 1em 2em; + background: rgba(255, 255, 255, 0.5); + font-size: 0.9em; + color: #4e4e4e; + cursor: pointer; + -webkit-transition: background 100ms ease-in-out; + -ms-transition: background 100ms ease-in-out; + transition: background 100ms ease-in-out; } + #listView button:hover { + background: #FFFFFF; } + #listView button:focus { + outline-style: solid; + outline-color: rgba(78, 214, 203, 0.5); + outline-width: 2px; + border: 1px solid #4ED6CB; } + #listView button.selected { + background: #4ED6CB; } + #listView button.selected:focus { + outline: 0; } + #listView button div { + width: 50%; } + .imageManager .form .row { display: flex; } .imageManager .form .row .label { @@ -553,7 +645,7 @@ .imageManager .manageManifests .manifestCard span { font-size: 0.8em; font-weight: normal; - color: rgba(78, 78, 78, 0.6); } + color: rgba(78, 78, 78, 0.8); } .imageManager .manageManifests .manifestCard > div { flex-grow: 1; } .imageManager .manageManifests .manifestCard .thumbnails { @@ -564,33 +656,35 @@ border-width: 0px 1px 0px 1px; border-style: solid; border-color: #F2F2F2; - cursor: move; + cursor: pointer; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; border-radius: 3px; - padding-left: 0.5em; } + padding-left: 0.5em; + display: flex; + justify-content: space-between; + align-items: center; } .imageManager .imageMapper .draggableItem .text { - display: inline-block; color: #4e4e4e; - padding-left: 0.5em; - position: relative; - top: 50%; - left: 0%; - transform: translateY(-50%) translateX(0%); } + padding-left: 0.5em; } .imageManager .imageMapper .draggableItem .text > span { font-size: 0.8em; color: rgba(78, 78, 78, 0.7); } .imageManager .imageMapper .draggableItem .thumbnail { opacity: 0.5; - display: inline-block; - width: 1.5em; -webkit-transition: all 200ms ease-in-out; -ms-transition: all 200ms ease-in-out; transition: all 200ms ease-in-out; } .imageManager .imageMapper .draggableItem .thumbnail:hover { opacity: 1; cursor: pointer; } +.imageManager .imageMapper .middleBar { + display: flex; + justify-content: space-around; + background: #FFFFFF; + margin: 0.5em 1em; + padding: 0.2em 0em; } .imageManager .imageMapper .panelBar { background: #FFFFFF; width: 100%; @@ -611,7 +705,6 @@ display: flex; flex-direction: column; width: 97%; - height: 40vh; margin-top: 1em; margin-left: 1em; background: #eaeaea; @@ -620,9 +713,6 @@ width: 100%; height: 100%; margin: 0em; } - .imageManager .imageMapper .topPanel .panelBarGroup { - height: 50px; - display: flex; } .imageManager .imageMapper .topPanel .boards { display: flex; width: 100%; @@ -630,18 +720,18 @@ .imageManager .imageMapper .topPanel .boards > div { width: 50%; } .imageManager .imageMapper .topPanel .binText { - position: relative; - top: 50%; - left: 50%; - transform: translateY(-50%) translateX(-50%); text-transform: uppercase; - text-align: center; } + text-align: center; + display: flex; + align-items: center; + justify-content: center; + width: 100% !important; + height: 38vh; } .imageManager .imageMapper .bottomPanel { flex-grow: 2; display: flex; justify-content: space-between; width: 97%; - margin-top: 1em; margin-left: 1em; } .imageManager .imageMapper .bottomPanel .backlog, .imageManager .imageMapper .bottomPanel .sideBacklog, .imageManager .imageMapper .bottomPanel .imageBacklog { width: 49%; @@ -670,24 +760,22 @@ .imageManager .imageMapper .bottomPanel .imageBacklog .scrollable { height: 27vh; } .imageManager .imageMapper .mainToolbar { - position: fixed; - bottom: 0; + margin-top: 0.5em; width: 100%; height: 50px; display: flex; + justify-content: space-between; align-items: center; background: #FFFFFF; - -webkit-box-shadow: 0px -2px 2px 0px rgba(0, 0, 0, 0.05); - box-shadow: 0px -2px 2px 0px rgba(0, 0, 0, 0.05); } + -webkit-box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.05); } .imageManager .imageMapper .mainToolbar .message { padding-left: 1em; text-transform: uppercase; color: #4e4e4e; font-size: 0.9em; } .imageManager .imageMapper .mainToolbar .actions { - text-align: right; } - .imageManager .imageMapper .mainToolbar > div { - width: 40%; } + padding-right: 1em; } .addDialog .title { color: #4e4e4e; } @@ -706,39 +794,59 @@ display: inline-block; text-align: right; } +.feedbackDialog p { + color: #4e4e4e; } .feedbackDialog .label { + color: #4e4e4e; width: 100px; display: inline-block; vertical-align: top; } .feedbackDialog .input { - width: 200px; + width: 250px; display: inline-block; text-align: right; } +.newProjectDialog p { + color: #4e4e4e; } .newProjectDialog h1 { font-weight: normal; text-transform: inherit; } +.newProjectDialog h2 { + font-size: 1em; } +.newProjectDialog .section { + margin-left: 2em; } +.newProjectDialog .newProjectSelection button.btnSelection { + width: 100%; + background: #FFFFFF; + border: 0; + -webkit-box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.2); + margin-bottom: 1em; + outline: 0; } + .newProjectDialog .newProjectSelection button.btnSelection:focus, .newProjectDialog .newProjectSelection button.btnSelection:hover { + outline-style: solid; + outline-width: 0.5em; + outline-color: rgba(78, 214, 203, 0.5); + cursor: pointer; } .newProjectDialog .newProjectSelection .selectItem { display: flex; - padding: 1.9em 0em; - -webkit-transition: all 200ms ease-in-out; - -ms-transition: all 200ms ease-in-out; - transition: all 200ms ease-in-out; - border: 2px solid #FFFFFF; } - .newProjectDialog .newProjectSelection .selectItem:hover { - border: 2px solid #4ED6CB; - cursor: pointer; } + padding: 2.5em 0em; } .newProjectDialog .newProjectSelection .selectItem .icon { width: 50px; padding: 0px 15px; } - .newProjectDialog .newProjectSelection .selectItem .text h1 { - font-size: 2em; - margin: 0; } - .newProjectDialog .newProjectSelection .selectItem .text h2 { - font-size: 1em; - color: #8e8e8e; - padding-top: 5px; - margin: 0; } + .newProjectDialog .newProjectSelection .selectItem .text { + line-height: 2.5em; } + .newProjectDialog .newProjectSelection .selectItem .text span:nth-child(1) { + text-align: left; + font-size: 2.5em; + display: block; + color: #4e4e4e; + width: 100%; } + .newProjectDialog .newProjectSelection .selectItem .text span:nth-child(2) { + font-size: 1.3em; + color: #747474; + width: 100%; + display: block; } .tooltip { position: relative; @@ -797,6 +905,13 @@ visibility: visible; opacity: 1; } +textarea { + border: 1px solid #e0e0e0; + width: 100%; + font-size: 1em; } + textarea:focus { + outline-color: #4ED6CB; } + h1 { font-size: 1.5em; text-transform: uppercase; diff --git a/viscoll-app/src/styles/App.css.map b/viscoll-app/src/styles/App.css.map index ea3e09fd..34430ed0 100644 --- a/viscoll-app/src/styles/App.css.map +++ b/viscoll-app/src/styles/App.css.map @@ -1,7 +1,7 @@ { "version": 3, -"mappings": "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", -"sources": ["../../sass/layout/_landing.scss","../../sass/lib/_variables.scss","../../sass/layout/_sidebar.scss","../../sass/lib/_mixins.scss","../../sass/layout/_projectPanel.scss","../../sass/layout/_infobox.scss","../../sass/layout/_workspace.scss","../../sass/layout/_tabular.scss","../../sass/layout/_topbar.scss","../../sass/layout/_notes.scss","../../sass/layout/_filter.scss","../../sass/layout/_loading.scss","../../sass/layout/_404.scss","../../sass/layout/_imageManager.scss","../../sass/components/_dialog.scss","../../sass/components/_tooltip.scss","../../sass/typography.scss","../../sass/index.scss"], +"mappings": "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", +"sources": ["../../sass/layout/_landing.scss","../../sass/lib/_variables.scss","../../sass/layout/_sidebar.scss","../../sass/lib/_mixins.scss","../../sass/layout/_projectPanel.scss","../../sass/layout/_infobox.scss","../../sass/layout/_workspace.scss","../../sass/layout/_tabular.scss","../../sass/layout/_topbar.scss","../../sass/layout/_notes.scss","../../sass/layout/_filter.scss","../../sass/layout/_loading.scss","../../sass/layout/_404.scss","../../sass/layout/_dashboard.scss","../../sass/layout/_imageManager.scss","../../sass/components/_dialog.scss","../../sass/components/_tooltip.scss","../../sass/components/_textarea.scss","../../sass/typography.scss","../../sass/index.scss"], "names": [], "file": "App.css" } \ No newline at end of file diff --git a/viscoll-app/src/styles/button.js b/viscoll-app/src/styles/button.js index bf8114c5..decc1abf 100644 --- a/viscoll-app/src/styles/button.js +++ b/viscoll-app/src/styles/button.js @@ -25,3 +25,13 @@ export let btnMd = { } } +export let btnAuthCancel = { + labelStyle: { + color: "#a5bde0", + } +} + +export let btnGreen = { + labelColor: "#ffffff", + backgroundColor: "#34A251", +} diff --git a/viscoll-app/src/styles/textfield.js b/viscoll-app/src/styles/textfield.js index 23f1d31f..ca52bd74 100644 --- a/viscoll-app/src/styles/textfield.js +++ b/viscoll-app/src/styles/textfield.js @@ -1,6 +1,6 @@ const floatFieldDark = { floatingLabelStyle: { - color: "#8dacd8", + color: "#a5bde0", }, underlineStyle: { border: "1px solid #526C91", @@ -12,5 +12,13 @@ const floatFieldDark = { color: "white", } } +const floatFieldLight = { + floatingLabelShrinkStyle: {color: "#526C91"}, + floatingLabelStyle: {color: "#6E6E6E"}, + hintStyle: {color: "#6E6E6E"}, +} -export default floatFieldDark; \ No newline at end of file +export { + floatFieldDark, + floatFieldLight, +} \ No newline at end of file From 3c14bd495eacf9f180b65163d7054a354bf0f438 Mon Sep 17 00:00:00 2001 From: Monica Ung Date: Fri, 12 Jan 2018 12:01:59 -0500 Subject: [PATCH 03/20] Deploy VisColl 0.8.0 to master - Undo-Redo feature. - Bug Fixes. - User feedback. - Increase test coverage for API endpoints. --- .gitignore | 6 + viscoll-api/Gemfile | 2 + viscoll-api/Gemfile.lock | 18 +- .../app/controllers/application_controller.rb | 9 +- .../app/controllers/export_controller.rb | 40 +- .../app/controllers/filter_controller.rb | 185 +- .../app/controllers/groups_controller.rb | 34 +- .../app/controllers/images_controller.rb | 251 + .../app/controllers/import_controller.rb | 16 +- .../app/controllers/leafs_controller.rb | 107 +- .../app/controllers/notes_controller.rb | 24 +- .../app/controllers/projects_controller.rb | 67 +- .../app/controllers/sides_controller.rb | 45 +- .../controller_helper/export_helper.rb | 517 +- .../controller_helper/filter_helper.rb | 114 +- .../controller_helper/groups_helper.rb | 56 +- .../controller_helper/import_json_helper.rb | 138 + .../import_mapping_helper.rb | 91 + .../controller_helper/import_xml_helper.rb | 378 + .../helpers/controller_helper/leafs_helper.rb | 22 +- .../controller_helper/projects_helper.rb | 66 +- viscoll-api/app/models/group.rb | 2 +- viscoll-api/app/models/image.rb | 40 + viscoll-api/app/models/leaf.rb | 10 +- viscoll-api/app/models/project.rb | 20 +- viscoll-api/app/models/side.rb | 12 +- viscoll-api/app/models/user.rb | 3 +- .../app/views/exports/show.json.jbuilder | 22 +- .../app/views/projects/index.json.jbuilder | 14 +- .../app/views/projects/show.json.jbuilder | 16 +- viscoll-api/config/application.rb | 4 +- .../config/environments/development.rb | 5 + viscoll-api/config/environments/test.rb | 6 + viscoll-api/config/mongoid.yml | 4 +- viscoll-api/config/routes.rb | 22 +- viscoll-api/config/secrets.yml | 2 + viscoll-api/public/viscoll-datamodel2.rng | 33 +- viscoll-api/spec/factories/groups.rb | 59 +- viscoll-api/spec/factories/images.rb | 25 + viscoll-api/spec/factories/leafs.rb | 13 + viscoll-api/spec/factories/notes.rb | 29 +- viscoll-api/spec/factories/projects.rb | 91 +- viscoll-api/spec/fixtures/dots_exported.zip | Bin 0 -> 781 bytes viscoll-api/spec/fixtures/pixel.png | Bin 0 -> 70 bytes .../spec/fixtures/sample_import_json.json | 301 + .../spec/fixtures/sample_import_xml.xml | 154 + viscoll-api/spec/fixtures/shibainu.jpg | Bin 0 -> 128103 bytes viscoll-api/spec/fixtures/viscoll.png | Bin 0 -> 64210 bytes .../controller_helper/export_helper_spec.rb | 82 +- .../controller_helper/filter_helper_spec.rb | 4 +- .../import_json_helper_spec.rb | 60 + .../import_mapping_helper_spec.rb | 61 + .../import_xml_helper_spec.rb | 61 + .../controller_helper/leafs_helper_spec.rb | 22 + .../controller_helper/projects_helper_spec.rb | 2 +- viscoll-api/spec/models/group_spec.rb | 1 + viscoll-api/spec/models/image_spec.rb | 46 + viscoll-api/spec/models/leaf_spec.rb | 4 + viscoll-api/spec/models/project_spec.rb | 15 + viscoll-api/spec/models/side_spec.rb | 11 +- .../requests/groups/groups_create_spec.rb | 10 +- .../groups/groups_destroy_multiple_spec.rb | 13 +- .../requests/groups/groups_destroy_spec.rb | 5 +- .../groups/groups_update_multiple_spec.rb | 5 +- .../requests/groups/groups_update_spec.rb | 5 +- .../requests/images/destroy_images_spec.rb | 134 + .../spec/requests/images/link_images_spec.rb | 253 + .../spec/requests/images/show_images_spec.rb | 64 + .../requests/images/unlink_images_spec.rb | 259 + .../requests/images/upload_images_spec.rb | 174 + .../spec/requests/images/zip_images_spec.rb | 64 + .../spec/requests/leafs/leafs_conjoin_spec.rb | 38 +- .../spec/requests/leafs/leafs_create_spec.rb | 5 +- .../leafs/leafs_destroy_multiple_spec.rb | 5 +- .../spec/requests/leafs/leafs_destroy_spec.rb | 5 +- .../leafs/leafs_update_multiple_spec.rb | 5 +- .../spec/requests/leafs/leafs_update_spec.rb | 5 +- .../spec/requests/notes/notes_create_spec.rb | 5 +- .../requests/notes/notes_create_type_spec.rb | 2 +- .../requests/notes/notes_delete_type_spec.rb | 2 +- .../spec/requests/notes/notes_destroy_spec.rb | 4 +- .../spec/requests/notes/notes_link_spec.rb | 10 +- .../spec/requests/notes/notes_unlink_spec.rb | 6 +- .../spec/requests/notes/notes_update_spec.rb | 4 +- .../requests/notes/notes_update_type_spec.rb | 2 +- .../requests/projects/create_projects_spec.rb | 4 +- .../projects/delete_manifest_projects_spec.rb | 5 +- .../projects/destroy_projects_spec.rb | 21 +- .../requests/projects/export_projects_spec.rb | 271 + .../requests/projects/filter_projects_spec.rb | 1018 +++ .../requests/projects/import_projects_spec.rb | 143 + .../requests/projects/index_projects_spec.rb | 6 +- .../projects/update_manifest_projects_spec.rb | 5 +- .../requests/projects/update_projects_spec.rb | 2 +- .../sides/sides_updateMultiplce_spec.rb | 4 +- .../spec/requests/sides/sides_update_spec.rb | 4 +- viscoll-api/spec/spec_helper.rb | 5 + .../groupActions.spec.js | 313 + .../helperActions.spec.js | 48 + .../imageActions.spec.js | 112 + .../frontendBeforeActions/leafActions.spec.js | 346 + .../manifestActions.spec.js | 69 + .../frontendBeforeActions/noteActions.spec.js | 250 + .../projectActions.spec.js | 94 + .../frontendBeforeActions/sideActions.spec.js | 148 + .../__test__/actions/projectActions.spec.js | 94 - .../__test__/actions/userActions.spec.js | 163 - .../helpers/MultiSelectAutoComplete.spec.js | 18 - .../structureRelatedReducers.spec.js | 328 - .../__test__/reducers/userReducer.spec.js | 220 - .../__test__/testData/dashboardState001.js | 78 + .../__test__/testData/projectState001.js | 4753 +++++++++++++ viscoll-app/__test__/testData/state001.js | 4753 +++++++++++++ .../assets/viscoll_component_tree_diagram.svg | 4 - .../docs/assets/viscoll_data_flow_diagram.svg | 4 - viscoll-app/docs/introduction.md | 5 - viscoll-app/package-lock.json | 6279 +++++++++++++---- viscoll-app/package.json | 12 +- viscoll-app/sass/components/_dialog.scss | 5 +- viscoll-app/sass/index.scss | 1 + viscoll-app/sass/layout/_imageCollection.scss | 7 + viscoll-app/sass/layout/_imageManager.scss | 42 +- viscoll-app/sass/layout/_infobox.scss | 9 + viscoll-app/sass/layout/_notes.scss | 10 +- viscoll-app/sass/layout/_sidebar.scss | 48 +- viscoll-app/sass/layout/_tabular.scss | 9 + viscoll-app/sass/layout/_topbar.scss | 9 +- viscoll-app/sass/layout/_workspace.scss | 1 - viscoll-app/sass/lib/_variables.scss | 6 +- viscoll-app/sass/typography.scss | 18 +- .../src/actions/backend/filterActions.js | 110 + .../src/actions/backend/groupActions.js | 74 + .../src/actions/backend/imageActions.js | 95 + .../interactionActions.js | 149 +- .../src/actions/backend/leafActions.js | 115 + .../src/actions/backend/manifestActions.js | 68 + .../src/actions/backend/noteActions.js | 177 + .../src/actions/backend/projectActions.js | 139 + .../src/actions/backend/sideActions.js | 29 + .../src/actions/{ => backend}/userActions.js | 1 + .../editCollation/modificationActions.js | 411 -- .../actions/frontend/after/imageActions.js | 14 + .../actions/frontend/before/groupActions.js | 123 + .../actions/frontend/before/helperActions.js | 9 + .../actions/frontend/before/imageActions.js | 94 + .../actions/frontend/before/leafActions.js | 246 + .../frontend/before/manifestActions.js | 25 + .../actions/frontend/before/noteActions.js | 115 + .../actions/frontend/before/projectActions.js | 26 + .../actions/frontend/before/sideActions.js | 69 + viscoll-app/src/actions/projectActions.js | 191 - .../src/assets/visualMode/PaperGroup.js | 7 +- .../src/assets/visualMode/PaperLeaf.js | 34 +- .../src/assets/visualMode/PaperManager.js | 123 +- .../src/components/authentication/Login.js | 14 - .../src/components/authentication/Login.md | 7 - .../src/components/authentication/Register.js | 9 - .../src/components/authentication/Register.md | 8 - .../authentication/ResetPassword.js | 10 - .../authentication/ResetPassword.md | 9 - .../authentication/ResetPasswordRequest.js | 7 - .../authentication/ResetPasswordRequest.md | 7 - .../collationManager/TabularMode.js | 9 +- .../collationManager/ViewingMode.js | 23 +- .../components/collationManager/VisualMode.js | 16 - .../collationManager/dialog/NoteDialog.js | 17 +- .../collationManager/tabularMode/Group.js | 19 +- .../collationManager/tabularMode/Leaf.js | 33 +- .../collationManager/tabularMode/Side.js | 11 +- .../src/components/dashboard/CloneProject.js | 38 +- .../components/dashboard/EditProjectForm.js | 44 +- .../components/dashboard/EditProjectForm.md | 9 - .../components/dashboard/ImageCollections.js | 308 + .../src/components/dashboard/ImportProject.js | 62 +- .../src/components/dashboard/ListView.js | 14 +- .../components/dashboard/ProjectStructure.js | 51 +- viscoll-app/src/components/export/Export.js | 71 +- .../src/components/filter/FilterRow.js | 122 +- .../src/components/global/AppLoadingScreen.js | 5 - .../src/components/global/ImageViewer.js | 30 +- .../src/components/global/LoadingScreen.js | 10 +- .../components/global/NetworkErrorScreen.js | 28 + .../src/components/global/Notification.js | 1 + .../src/components/global/SelectField.js | 85 + .../components/global/ServerErrorScreen.js | 50 + .../global/UnauthorizedErrorScreen.js | 50 + .../components/imageManager/AddManifest.js | 115 +- .../components/imageManager/EditManifest.js | 222 +- .../imageManager/ManageManifests.js | 56 +- .../src/components/imageManager/MapImages.js | 91 +- .../imageManager/RemoveImageConfirmation.js | 66 + .../components/imageManager/UploadImages.js | 174 + .../imageManager/mapImages/ImageBacklog.js | 10 +- .../imageManager/mapImages/ImageItem.js | 5 +- .../imageManager/mapImages/MapBoard.js | 36 +- .../imageManager/mapImages/SideBacklog.js | 4 +- .../src/components/infoBox/GroupInfoBox.js | 96 +- .../src/components/infoBox/LeafInfoBox.js | 185 +- .../src/components/infoBox/SideInfoBox.js | 100 +- .../infoBox/dialog/AddGroupDialog.js | 55 +- .../infoBox/dialog/AddGroupDialog.md | 16 - .../infoBox/dialog/AddLeafDialog.js | 40 +- .../infoBox/dialog/AddLeafDialog.md | 11 - .../src/components/infoBox/dialog/AddNote.js | 2 +- .../dialog/DeleteConfirmationDialog.js | 30 +- .../dialog/DeleteConfirmationDialog.md | 5 - .../infoBox/dialog/FolioNumberDialog.js | 167 + .../infoBox/dialog/VisualizationDialog.js | 64 +- .../notesManager/DeleteConfirmation.js | 9 - .../components/notesManager/EditNoteForm.js | 297 +- .../components/notesManager/ManageNotes.js | 35 +- .../components/notesManager/NewNoteForm.js | 26 +- .../src/components/notesManager/NoteType.js | 4 +- .../components/notesManager/NotesFilter.js | 8 +- .../src/components/topbar/UserProfileForm.js | 19 - .../src/components/topbar/UserProfileForm.md | 16 - viscoll-app/src/containers/App.js | 4 +- viscoll-app/src/containers/Authentication.js | 19 +- viscoll-app/src/containers/Authentication.md | 13 - .../src/containers/CollationManager.js | 185 +- viscoll-app/src/containers/Dashboard.js | 179 +- viscoll-app/src/containers/Dashboard.md | 9 - viscoll-app/src/containers/Feedback.js | 6 +- viscoll-app/src/containers/Filter.js | 95 +- viscoll-app/src/containers/ImageManager.js | 98 +- viscoll-app/src/containers/InfoBox.js | 190 +- viscoll-app/src/containers/InfoBox.md | 6 - viscoll-app/src/containers/NotesManager.js | 78 +- viscoll-app/src/containers/Project.js | 26 +- viscoll-app/src/containers/Project.md | 6 - viscoll-app/src/containers/TopBar.js | 42 +- viscoll-app/src/containers/TopBar.md | 5 - viscoll-app/src/helpers/getLeafsOfGroup.js | 12 +- viscoll-app/src/helpers/getMemberOrder.js | 10 + ...{projectReducer.js => dashboardReducer.js} | 41 +- .../src/reducers/editCollationReducer.js | 355 +- viscoll-app/src/reducers/globalReducer.js | 11 +- .../src/reducers/initialStates/active.js | 13 +- .../src/reducers/initialStates/global.js | 4 +- .../src/reducers/initialStates/projects.js | 1 + viscoll-app/src/reducers/userReducer.js | 14 +- viscoll-app/src/store.js | 39 - viscoll-app/src/{ => store}/axiosConfig.js | 27 +- .../frontendAfterActionsMiddleware.js | 20 + .../frontendBeforeActionsMiddleware.js | 175 + viscoll-app/src/store/store.js | 45 + viscoll-app/src/styles/App.css | 159 +- viscoll-app/src/styles/App.css.map | 4 +- viscoll-app/src/styles/button.js | 43 +- viscoll-app/src/styles/checkbox.js | 21 + viscoll-app/src/styles/infobox.js | 10 +- viscoll-app/src/styles/topbar.js | 21 +- viscoll-app/styleguide.config.js | 54 - 253 files changed, 26147 insertions(+), 5748 deletions(-) create mode 100644 viscoll-api/app/controllers/images_controller.rb create mode 100644 viscoll-api/app/helpers/controller_helper/import_json_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/import_mapping_helper.rb create mode 100644 viscoll-api/app/helpers/controller_helper/import_xml_helper.rb create mode 100644 viscoll-api/app/models/image.rb create mode 100644 viscoll-api/spec/factories/images.rb create mode 100644 viscoll-api/spec/fixtures/dots_exported.zip create mode 100644 viscoll-api/spec/fixtures/pixel.png create mode 100644 viscoll-api/spec/fixtures/sample_import_json.json create mode 100644 viscoll-api/spec/fixtures/sample_import_xml.xml create mode 100644 viscoll-api/spec/fixtures/shibainu.jpg create mode 100644 viscoll-api/spec/fixtures/viscoll.png create mode 100644 viscoll-api/spec/helpers/controller_helper/import_json_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/import_mapping_helper_spec.rb create mode 100644 viscoll-api/spec/helpers/controller_helper/import_xml_helper_spec.rb create mode 100644 viscoll-api/spec/models/image_spec.rb create mode 100644 viscoll-api/spec/requests/images/destroy_images_spec.rb create mode 100644 viscoll-api/spec/requests/images/link_images_spec.rb create mode 100644 viscoll-api/spec/requests/images/show_images_spec.rb create mode 100644 viscoll-api/spec/requests/images/unlink_images_spec.rb create mode 100644 viscoll-api/spec/requests/images/upload_images_spec.rb create mode 100644 viscoll-api/spec/requests/images/zip_images_spec.rb create mode 100644 viscoll-api/spec/requests/projects/export_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/filter_projects_spec.rb create mode 100644 viscoll-api/spec/requests/projects/import_projects_spec.rb create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/groupActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/helperActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/imageActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/leafActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/manifestActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/noteActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/projectActions.spec.js create mode 100644 viscoll-app/__test__/actions/frontendBeforeActions/sideActions.spec.js delete mode 100644 viscoll-app/__test__/actions/projectActions.spec.js delete mode 100644 viscoll-app/__test__/actions/userActions.spec.js delete mode 100644 viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js delete mode 100644 viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js delete mode 100644 viscoll-app/__test__/reducers/userReducer.spec.js create mode 100644 viscoll-app/__test__/testData/dashboardState001.js create mode 100644 viscoll-app/__test__/testData/projectState001.js create mode 100644 viscoll-app/__test__/testData/state001.js delete mode 100644 viscoll-app/docs/assets/viscoll_component_tree_diagram.svg delete mode 100644 viscoll-app/docs/assets/viscoll_data_flow_diagram.svg delete mode 100644 viscoll-app/docs/introduction.md create mode 100644 viscoll-app/sass/layout/_imageCollection.scss create mode 100644 viscoll-app/src/actions/backend/filterActions.js create mode 100644 viscoll-app/src/actions/backend/groupActions.js create mode 100644 viscoll-app/src/actions/backend/imageActions.js rename viscoll-app/src/actions/{editCollation => backend}/interactionActions.js (57%) create mode 100644 viscoll-app/src/actions/backend/leafActions.js create mode 100644 viscoll-app/src/actions/backend/manifestActions.js create mode 100644 viscoll-app/src/actions/backend/noteActions.js create mode 100644 viscoll-app/src/actions/backend/projectActions.js create mode 100644 viscoll-app/src/actions/backend/sideActions.js rename viscoll-app/src/actions/{ => backend}/userActions.js (99%) delete mode 100644 viscoll-app/src/actions/editCollation/modificationActions.js create mode 100644 viscoll-app/src/actions/frontend/after/imageActions.js create mode 100644 viscoll-app/src/actions/frontend/before/groupActions.js create mode 100644 viscoll-app/src/actions/frontend/before/helperActions.js create mode 100644 viscoll-app/src/actions/frontend/before/imageActions.js create mode 100644 viscoll-app/src/actions/frontend/before/leafActions.js create mode 100644 viscoll-app/src/actions/frontend/before/manifestActions.js create mode 100644 viscoll-app/src/actions/frontend/before/noteActions.js create mode 100644 viscoll-app/src/actions/frontend/before/projectActions.js create mode 100644 viscoll-app/src/actions/frontend/before/sideActions.js delete mode 100644 viscoll-app/src/actions/projectActions.js delete mode 100644 viscoll-app/src/components/authentication/Login.md delete mode 100644 viscoll-app/src/components/authentication/Register.md delete mode 100644 viscoll-app/src/components/authentication/ResetPassword.md delete mode 100644 viscoll-app/src/components/authentication/ResetPasswordRequest.md delete mode 100644 viscoll-app/src/components/dashboard/EditProjectForm.md create mode 100644 viscoll-app/src/components/dashboard/ImageCollections.js create mode 100644 viscoll-app/src/components/global/NetworkErrorScreen.js create mode 100644 viscoll-app/src/components/global/SelectField.js create mode 100644 viscoll-app/src/components/global/ServerErrorScreen.js create mode 100644 viscoll-app/src/components/global/UnauthorizedErrorScreen.js create mode 100644 viscoll-app/src/components/imageManager/RemoveImageConfirmation.js create mode 100644 viscoll-app/src/components/imageManager/UploadImages.js delete mode 100644 viscoll-app/src/components/infoBox/dialog/AddGroupDialog.md delete mode 100644 viscoll-app/src/components/infoBox/dialog/AddLeafDialog.md delete mode 100644 viscoll-app/src/components/infoBox/dialog/DeleteConfirmationDialog.md create mode 100644 viscoll-app/src/components/infoBox/dialog/FolioNumberDialog.js delete mode 100644 viscoll-app/src/components/topbar/UserProfileForm.md delete mode 100644 viscoll-app/src/containers/Authentication.md delete mode 100644 viscoll-app/src/containers/Dashboard.md delete mode 100644 viscoll-app/src/containers/InfoBox.md delete mode 100644 viscoll-app/src/containers/Project.md delete mode 100644 viscoll-app/src/containers/TopBar.md create mode 100644 viscoll-app/src/helpers/getMemberOrder.js rename viscoll-app/src/reducers/{projectReducer.js => dashboardReducer.js} (56%) delete mode 100644 viscoll-app/src/store.js rename viscoll-app/src/{ => store}/axiosConfig.js (52%) create mode 100644 viscoll-app/src/store/middleware/frontendAfterActionsMiddleware.js create mode 100644 viscoll-app/src/store/middleware/frontendBeforeActionsMiddleware.js create mode 100644 viscoll-app/src/store/store.js create mode 100644 viscoll-app/src/styles/checkbox.js delete mode 100644 viscoll-app/styleguide.config.js diff --git a/.gitignore b/.gitignore index 978ba3dc..3d413911 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ /public/swagger/ +*.idea + # React app stuff # dependencies @@ -57,3 +59,7 @@ coverage jest_0 test.log *.xml +!/viscoll-api/spec/fixtures/*.xml + +# DIY images +viscoll-api/uploads/* diff --git a/viscoll-api/Gemfile b/viscoll-api/Gemfile index b88617fa..137c1a61 100644 --- a/viscoll-api/Gemfile +++ b/viscoll-api/Gemfile @@ -47,6 +47,8 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'mongoid', '~> 6.2' gem 'rails_jwt_auth', '~> 0.16.1' +gem "mongoid-paperclip" +gem 'rubyzip' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible gem 'rack-cors', '~> 0.4.1' diff --git a/viscoll-api/Gemfile.lock b/viscoll-api/Gemfile.lock index df3c02ad..19fc7bb5 100644 --- a/viscoll-api/Gemfile.lock +++ b/viscoll-api/Gemfile.lock @@ -54,6 +54,9 @@ GEM bson (4.2.1) builder (3.2.3) byebug (9.0.6) + climate_control (0.2.0) + cocaine (0.5.8) + climate_control (>= 0.0.3, < 1.0) coderay (1.1.1) concurrent-ruby (1.0.5) crack (0.4.3) @@ -106,6 +109,7 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mimemagic (0.3.2) mini_portile2 (2.2.0) minitest (5.10.2) mongo (2.4.2) @@ -113,6 +117,9 @@ GEM mongoid (6.2.0) activemodel (~> 5.1) mongo (>= 2.4.1, < 3.0.0) + mongoid-paperclip (0.0.11) + mongoid + paperclip (>= 2.3.6, != 4.3.0) multi_json (1.12.1) nenv (0.3.0) nio4r (2.1.0) @@ -121,6 +128,12 @@ GEM notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) + paperclip (5.1.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + cocaine (~> 0.5.5) + mime-types + mimemagic (~> 0.3.0) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -186,6 +199,7 @@ GEM rspec-support (3.6.0) rspec_junit_formatter (0.3.0) rspec-core (>= 2, < 4, != 2.12.0) + rubyzip (1.2.1) safe_yaml (1.0.4) shellany (0.0.1) shoulda-matchers (3.1.1) @@ -234,6 +248,7 @@ DEPENDENCIES jbuilder (~> 2.7) listen (~> 3.0.5) mongoid (~> 6.2) + mongoid-paperclip mongoid-rspec! puma (~> 3.0) rack-cors (~> 0.4.1) @@ -241,6 +256,7 @@ DEPENDENCIES rails_jwt_auth (~> 0.16.1) rspec-rails (~> 3.6) rspec_junit_formatter (~> 0.3.0) + rubyzip shoulda-matchers (~> 3.1, >= 3.1.1) simplecov spring @@ -249,4 +265,4 @@ DEPENDENCIES webmock (~> 3.1.0) BUNDLED WITH - 1.14.6 + 1.16.0 diff --git a/viscoll-api/app/controllers/application_controller.rb b/viscoll-api/app/controllers/application_controller.rb index 98bc77b2..553e9438 100644 --- a/viscoll-api/app/controllers/application_controller.rb +++ b/viscoll-api/app/controllers/application_controller.rb @@ -1,10 +1,17 @@ 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::ImportHelper + include ControllerHelper::ImportJsonHelper + include ControllerHelper::ImportXmlHelper + include ControllerHelper::ImportMappingHelper include ControllerHelper::ExportHelper include ValidationHelper::ProjectValidationHelper include ValidationHelper::GroupValidationHelper diff --git a/viscoll-api/app/controllers/export_controller.rb b/viscoll-api/app/controllers/export_controller.rb index 77a3c6c8..88df03ea 100644 --- a/viscoll-api/app/controllers/export_controller.rb +++ b/viscoll-api/app/controllers/export_controller.rb @@ -1,16 +1,50 @@ +require 'zip' + class ExportController < ApplicationController before_action :authenticate! before_action :set_project, only: [:show] - # PUT /projects/id/export/format + # 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 = images[0].image.path.split("/") + basePath.pop + basePath = basePath.join("/") + zipFilename = basePath+'/'+@project.id.to_s+'_images.zip' + File.delete(zipFilename) if File.exist?(zipFilename) + ::Zip::File.open(zipFilename, Zip::File::CREATE) do |zip_file| + images.each do |image| + zip_file.add(image.id.to_s+"_"+image.filename, image.image.path) + end + end + @zipFilePath = @base_api_url+"/images/zip/"+images[0].id.to_s+"_"+@project.id.to_s + end + rescue Exception => e + end + begin case @format when "xml" exportData = buildDotModel(@project) - render json: {data: exportData, type: @format}, status: :ok + 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 + else + render json: {data: errors, type: @format}, status: :unprocessable_entity + end when "json" - @data = buildJSON(@project) + @data = buildJSON(@project) render :'exports/show', status: :ok else render json: {error: "Export format must be one of [json, xml]"}, status: :unprocessable_entity diff --git a/viscoll-api/app/controllers/filter_controller.rb b/viscoll-api/app/controllers/filter_controller.rb index 9ea1a833..257a64e9 100644 --- a/viscoll-api/app/controllers/filter_controller.rb +++ b/viscoll-api/app/controllers/filter_controller.rb @@ -50,6 +50,7 @@ def performFilter(queries) conjunctions = [] queries.each do |query| type = query[:type] + old_attribute = nil attribute = query[:attribute] condition = query[:condition] values = query[:values] @@ -58,160 +59,62 @@ def performFilter(queries) 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" - case condition - when "equals" - if values.length > 1 - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$in": values}) - else - groupQueryResult = @project.groups.only(:id).where("#{attribute}": values[0]) - end - when "not equals" - if values.length > 1 - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$nin": values}) - else - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$ne": values[0]}) - end - when "contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$in": values}) - else - groupQueryResult = @project.groups.only(:id).where("#{attribute}": /#{values[0]}/) - end - when "not contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$nin": values}) - else - groupQueryResult = @project.groups.only(:id).where("#{attribute}": {"$not": /#{values[0]}/}) - end - end - groupQueryResult.each do |leafID| - groups.push(leafID.id.to_s) - end - @objectIDs[:Groups] = @objectIDs[:Groups] + groups + 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" - if attribute == "conjoined_leaf_order" - old_attribute = attribute - attribute = "conjoined_to" - end - case condition - when "equals" - if values.length > 1 - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$in": values}) - else - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": values[0]) - end - when "not equals" - if values.length > 1 - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$nin": values}) - else - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$ne": values[0]}) - end - when "contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$in": values}) - else - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": /#{values[0]}/) - end - when "not contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$nin": values}) - else - leafQueryResult = @project.leafs.only(:id).where("#{attribute}": {"$not": /#{values[0]}/}) - end - end - leafQueryResult.each do |leafID| - leafs.push(leafID.id.to_s) + @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 + @visibleAttributes[:leaf][old_attribute] = true else - @visibleAttributes[:leaf]["#{attribute}"] = true + @visibleAttributes[:leaf][attribute] = true end end - @objectIDs[:Leafs] = @objectIDs[:Leafs] + leafs - when "side" - @project.sides.each do |side| - sides.push(side.id.to_s) - end - case condition - when "equals" - if values.length > 1 - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$in": values}) - else - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": values[0]) - end - when "not equals" - if values.length > 1 - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$nin": values}) - else - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$ne": values[0]}) - end - when "contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$in": values}) - else - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": /#{values[0]}/) - end - when "not contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$nin": values}) - else - sideQueryResult = Side.where(id: {"$in": sides}, "#{attribute}": {"$not": /#{values[0]}/}) - end - end - sides = [] + @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] = @objectIDs[:Sides] + sides - when "note" - case condition - when "equals" - if values.length > 1 - noteQueryResult = Note.where("#{attribute}": {"$in": values}) - else - noteQueryResult = Note.where("#{attribute}": values[0]) - end - when "not equals" - if values.length > 1 - noteQueryResult = Note.where("#{attribute}": {"$nin": values}) - else - noteQueryResult = Note.where("#{attribute}": {"$ne": values[0]}) - end - when "contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - noteQueryResult = Note.where("#{attribute}": {"$in": values}) - else - noteQueryResult = Note.where("#{attribute}": /#{values[0]}/) - end - when "not contains" - if values.length > 1 - values = values.map {|x| /^#{x}/} - noteQueryResult = Note.where("#{attribute}": {"$nin": values}) - else - noteQueryResult = Note.where("#{attribute}": {"$not": /#{values[0]}/}) - end - end - noteQueryResult.each do |noteID| - notes.push(noteID.id.to_s) + @visibleAttributes[:side][attribute] = true end - @objectIDs[:Notes] = @objectIDs[:Notes] + notes + @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) diff --git a/viscoll-api/app/controllers/groups_controller.rb b/viscoll-api/app/controllers/groups_controller.rb index e79ca1a9..44041e69 100644 --- a/viscoll-api/app/controllers/groups_controller.rb +++ b/viscoll-api/app/controllers/groups_controller.rb @@ -11,6 +11,9 @@ def create 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] # Validate group parameters @@ -40,6 +43,7 @@ def create end new_groups = [] new_group_ids = [] + groupIDIndex = 0 parent_group = nil if parentGroupID != nil parent_group = @project.groups.find(parentGroupID) @@ -47,6 +51,9 @@ def create # 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 @@ -66,14 +73,15 @@ def create # 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 do |group| + new_groups.each_with_index do |group, index| if noOfLeafs - addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) + 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 - @project = Project.find(project_id) - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -82,10 +90,7 @@ def create # PATCH/PUT /groups/1 def update begin - if @group.update(group_params) - @data = generateResponse() - render :'projects/show', status: :ok - else + if !@group.update(group_params) render json: @group.errors, status: :unprocessable_entity end rescue Exception => e @@ -115,9 +120,6 @@ def updateMultiple return end end - @project = Project.find(@group.project_id) - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -128,9 +130,6 @@ def destroy begin @group = Group.find(params[:id]) @group.destroy - @project = Project.find(@group.project_id) - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -156,9 +155,6 @@ def destroyMultiple next end end - @project = Project.find(projectID) - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -185,7 +181,7 @@ def group_params end def additional_params - params.require(:additional).permit(:order, :noOfGroups, :memberOrder, :parentGroupID, :noOfLeafs, :conjoin, :oddMemberLeftOut) + params.require(:additional).permit(:order, :noOfGroups, :memberOrder, :parentGroupID, :noOfLeafs, :conjoin, :oddMemberLeftOut, :groupIDs=>[], :leafIDs=>[], :sideIDs=>[]) end def group_params_batch_update diff --git a/viscoll-api/app/controllers/images_controller.rb b/viscoll-api/app/controllers/images_controller.rb new file mode 100644 index 00000000..9d3e5f8a --- /dev/null +++ b/viscoll-api/app/controllers/images_controller.rb @@ -0,0 +1,251 @@ +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 + 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 + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + return + end + newImages = [] + allImages = image_create_params.to_h[:images] + allImages.each do |image_data| + image = Paperclip.io_adapters.for(image_data[:content]) + filename = image_data[:filename].parameterize.underscore + image.original_filename = filename + image = Image.new(user: current_user, filename: filename, image: image, projectIDs: projectIDs) + image.filename = "#{image.filename}.#{image.image_content_type.split('/')[1]}" + image.image_file_name = filename + 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)" + image = Paperclip.io_adapters.for(image_data[:content]) + filename = "#{image_data[:filename].parameterize.underscore}_copy(#{copyCounter})" + image.original_filename = filename + image = Image.new(user: current_user, filename: filename, image: image, projectIDs: projectIDs) + image.filename = "#{image.filename}.#{image.image_content_type.split('/')[1]}" + image.image_file_name = filename + copyCounter += 1 + else + render json: image.errors, status: :unprocessable_entity + return + end + end + end + newImages.push(image) + end + @projects = current_user.projects + @images = newImages + render :'projects/index', status: :ok + 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 + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + return + end + send_file @image.image.path, :type => @image.image_content_type, :disposition => 'inline' + end + + + # GET /images/zip/:imageID_projectID + def getZipImages + begin + imageID = params[:id].split("_")[0] + projectID = params[:id].split("_")[1] + @image = Image.find(imageID) + imagePath = @image.image.path.split("/") + imagePath.pop + imagePath = imagePath.join("/") + zipFilePath = imagePath+"/"+projectID+"_images.zip" + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + return + end + send_file zipFilePath, :type => 'application/zip', :disposition => 'inline' + 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 + return + end + projects.push(project) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "project not found with id #{projectID}"}, status: :not_found + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + images.push(image) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID}"}, status: :not_found + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + 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 + return + end + projects.push(project) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "project not found with id #{projectID}"}, status: :not_found + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + return + end + images.push(image) + rescue Mongoid::Errors::DocumentNotFound + render json: {error: "image not found with id #{imageID.split("_", 2)[0]}"}, status: :not_found + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + 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 + 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 + return + rescue Exception => e + render json: {error: e.message}, status: :unprocessable_entity + return + end + if (image.user_id!=current_user.id) + render json: {error: ""}, status: :unauthorized + return + end + end + images.each do |image| + image.destroy + end + @projects = current_user.projects + @images = current_user.images + render :'projects/index', status: :ok + 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 index 27511670..70f42e34 100644 --- a/viscoll-api/app/controllers/import_controller.rb +++ b/viscoll-api/app/controllers/import_controller.rb @@ -1,11 +1,12 @@ class ImportController < ApplicationController before_action :authenticate! - # POST /projects/import + # 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" @@ -15,14 +16,17 @@ def index schema = Nokogiri::XML::RelaxNG(File.open("public/viscoll-datamodel2.rng")) errors = schema.validate(xml) if errors.empty? - handleXMLImport(Hash.from_xml(importData)["viscoll"]["manuscript"], xml) + handleXMLImport(xml) else render json: {error: errors}, status: :unprocessable_entity return end end - # render json: {error: "RETURING ERROR FOR NOW"}, status: :unprocessable_entity + 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 rescue Exception => e render json: {error: errorMessage}, status: :unprocessable_entity @@ -35,11 +39,7 @@ def index private # Never trust parameters from the scary Internet, only allow the white list through. def imported_data - params.permit(:importData, :importFormat) + 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 index c4c5d0b1..3963a312 100644 --- a/viscoll-api/app/controllers/leafs_controller.rb +++ b/viscoll-api/app/controllers/leafs_controller.rb @@ -8,6 +8,8 @@ def create 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] @@ -38,44 +40,54 @@ def create return end - newlyAddedLeafIDs = [] - newlyAddedLeafs = [] - noOfLeafs.times do |noOfLeafsIndex| - @leaf = Leaf.new(leaf_params) - @leaf.nestLevel = @group.nestLevel - if @leaf.save - newlyAddedLeafs.push(@leaf) - newlyAddedLeafIDs.push(@leaf.id) - else - render json: {leaf: @leaf.errors}, status: :unprocessable_entity - return - end - end - - # Time to Auto-Conjoin - newlyAddedLeafs = newlyAddedLeafs.reverse - if conjoin - if newlyAddedLeafs.size.odd? - newlyAddedLeafs.delete_at(oddMemberLeftOut-1) + # 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 - newlyAddedLeafs.size.times do |i| - if (newlyAddedLeafs.size/2 == i) - break + 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 - leafOne = newlyAddedLeafs[i] - leafTwo = newlyAddedLeafs[newlyAddedLeafs.size-i-1] - leafOne.update(conjoined_to: leafTwo.id.to_s) - leafTwo.update(conjoined_to: leafOne.id.to_s) + render json: {leaf: @leaf.errors}, status: :unprocessable_entity + 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) - # SUCCESS - @data = generateResponse() - render :'projects/show', status: :ok end @@ -89,8 +101,6 @@ def update if (leaf_params.to_h.key?(:attached_below)||leaf_params.to_h.key?(:attached_above)) update_attached_to() end - @data = generateResponse() - render :'projects/show', status: :ok else render json: {leaf: @leaf.errors}, status: :unprocessable_entity end @@ -126,8 +136,6 @@ def updateMultiple update_attached_to() end end - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -155,8 +163,6 @@ def destroy end @leaf.remove_from_group() @leaf.destroy - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -210,8 +216,6 @@ def destroyMultiple parentAndChildren.each do |parentID, leafIDs| @project.groups.find(parentID).remove_members(leafIDs) end - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end @@ -249,14 +253,29 @@ def conjoinLeafs return end @project = Project.find(leaves[0].project_id) - autoConjoinLeaves(leaves, leaves.length/2) - @data = generateResponse() - render :'projects/show', status: :ok + autoConjoinLeaves(leaves, (leaves.length+1)/2) rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end end + # PUT /leafs/generateFolio + def generateFolio + folioNumberCount = leaf_params_folio.to_h[:startNumber].to_i + rectoIDs = leaf_params_folio.to_h[:rectoIDs] + versoIDs = leaf_params_folio.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 + private # Use callbacks to share common setup or constraints between actions. @@ -276,11 +295,11 @@ def set_leaf end # Never trust parameters from the scary internet, only allow the white list through. def leaf_params - params.require(:leaf).permit(:project_id, :parentID, :material, :type, :attachment_method, :conjoined_to, :stub, :attached_above, :attached_below) + params.require(:leaf).permit(:id, :project_id, :parentID, :material, :type, :attachment_method, :conjoined_to, :stub, :attached_above, :attached_below) end def additional_params - params.require(:additional).permit(:memberOrder, :noOfLeafs, :conjoin, :oddMemberLeftOut) + params.require(:additional).permit(:memberOrder, :noOfLeafs, :conjoin, :oddMemberLeftOut, :leafIDs=>[], :sideIDs=>[]) end def leaf_params_batch_update @@ -295,4 +314,8 @@ def leaf_params_conjoin params.permit(:leafs => []) end + def leaf_params_folio + params.permit(:startNumber, :rectoIDs => [], :versoIDs => []) + end + end diff --git a/viscoll-api/app/controllers/notes_controller.rb b/viscoll-api/app/controllers/notes_controller.rb index 25f11531..c6c54537 100644 --- a/viscoll-api/app/controllers/notes_controller.rb +++ b/viscoll-api/app/controllers/notes_controller.rb @@ -22,8 +22,6 @@ def create @note.delete return end - @data = generateResponse() - render :'projects/show', status: :ok else render json: @note.errors, status: :unprocessable_entity end @@ -36,10 +34,7 @@ def update render json: {type: "should be one of " +Project.find(@note.project_id).noteTypes.to_s}, status: :unprocessable_entity return end - if @note.update(note_update_params) - @data = generateResponse() - render :'projects/show', status: :ok - else + if !@note.update(note_update_params) render json: @note.errors, status: :unprocessable_entity end end @@ -47,8 +42,6 @@ def update # DELETE /notes/1 def destroy @note.destroy - @data = generateResponse() - render :'projects/show', status: :ok end # PUT /notes/1/link @@ -66,8 +59,7 @@ def link when "Leaf" @object = Leaf.find(id) authorized = @object.project.user_id == current_user.id - when "Side" - type = id[0]=="R" ? "Recto" : "Verso" + when "Recto", "Verso" @object = Side.find(id) authorized = @object.project.user_id == current_user.id else @@ -93,8 +85,6 @@ def link render json: {error: e.message}, status: :unprocessable_entity return end - @data = generateResponse() - render :'projects/show', status: :ok end # PUT /notes/1/unlink @@ -136,8 +126,6 @@ def unlink render json: {error: e.message}, status: :unprocessable_entity return end - @data = generateResponse() - render :'projects/show', status: :ok end @@ -152,8 +140,6 @@ def createType @project.noteTypes.push(type) @project.save end - @data = generateResponse() - render :'projects/show', status: :ok end @@ -171,8 +157,6 @@ def deleteType note.save end end - @data = generateResponse() - render :'projects/show', status: :ok end @@ -195,8 +179,6 @@ def updateType note.save end end - @data = generateResponse() - render :'projects/show', status: :ok end @@ -234,7 +216,7 @@ def set_attached_project # Never trust parameters from the scary internet, only allow the white list through. def note_create_params - params.require(:note).permit(:project_id, :title, :type, :description, :show) + params.require(:note).permit(:project_id, :id, :title, :type, :description, :show) end def note_update_params diff --git a/viscoll-api/app/controllers/projects_controller.rb b/viscoll-api/app/controllers/projects_controller.rb index cdc16fe8..1df0721d 100644 --- a/viscoll-api/app/controllers/projects_controller.rb +++ b/viscoll-api/app/controllers/projects_controller.rb @@ -1,21 +1,22 @@ class ProjectsController < ApplicationController - before_action :authenticate! - before_action :set_project, only: [:show, :update, :destroy, :createManifest, :updateManifest, :deleteManifest] + 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 - begin + # begin @data = generateResponse() - rescue Exception => e - render json: {error: e.message}, status: :unprocessable_entity - return - end + # rescue Exception => e + # render json: {error: e.message}, status: :unprocessable_entity + # return + # end end # POST /projects @@ -43,6 +44,7 @@ def create 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 else render json: {project: @project.errors}, status: :unprocessable_entity @@ -58,6 +60,7 @@ def update @project = Project.find(params[:id]) if @project.update(project_params) @projects = current_user.projects + @images = current_user.images render :index, status: :ok else render json: {project: @project.errors}, status: :unprocessable_entity @@ -69,16 +72,25 @@ def update # 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 rescue Exception => e render json: {errors: e.message}, status: :bad_request ensure # Enable callbacks again + Image.set_callback(:destroy, :before, :unlink_sides_before_delete) Leaf.set_callback(:destroy, :before, :unlink_notes) end end @@ -112,8 +124,6 @@ def updateManifest # ONLY UPDATING MANIFEST NAME FOR NOW @project.manifests[manifest["id"]]["name"] = manifest["name"] @project.save - @data = generateResponse() - render :show, status: :ok rescue Exception => e render json: {errors: e.message}, status: :bad_request end @@ -135,13 +145,44 @@ def deleteManifest end end @project.save - @data = generateResponse() - render :show, status: :ok rescue Exception => e render json: {errors: e.message}, status: :bad_request 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 + rescue Exception => e + p e.message + end + end + + private def set_project begin @@ -161,6 +202,10 @@ 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(:groups => [:number, :leaves, :conjoin, :oddLeaf]) end diff --git a/viscoll-api/app/controllers/sides_controller.rb b/viscoll-api/app/controllers/sides_controller.rb index dcecc77e..a3922000 100644 --- a/viscoll-api/app/controllers/sides_controller.rb +++ b/viscoll-api/app/controllers/sides_controller.rb @@ -5,10 +5,7 @@ class SidesController < ApplicationController # PATCH/PUT /sides/1 def update begin - if @side.update(side_params) - @data = generateResponse() - render :'projects/show', status: :ok - else + if !@side.update(side_params) render json: @side.errors, status: :unprocessable_entity end rescue Exception => e @@ -45,13 +42,49 @@ def updateMultiple 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 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 + imageUnlikned = Image.find(imageID) + if !imageLinked or imageLinked.id.to_s != imageUnlikned.id.to_s + imageUnlikned.sideIDs.include?(side.id.to_s) ? imageUnlikned.sideIDs.delete(side.id.to_s) : nil + imageUnlikned.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 - @data = generateResponse() - render :'projects/show', status: :ok rescue Exception => e render json: {error: e.message}, status: :unprocessable_entity end diff --git a/viscoll-api/app/helpers/controller_helper/export_helper.rb b/viscoll-api/app/helpers/controller_helper/export_helper.rb index 93565328..602e5781 100644 --- a/viscoll-api/app/helpers/controller_helper/export_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/export_helper.rb @@ -162,13 +162,261 @@ def getLeafMemberOrders(memberIDs) def buildDotModel(project) @groupIDs = project.groupIDs + @groups = {} @leafIDs = [] @leafs = {} - @groups = {} @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'].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'].each do |attribute| + sideAttribute = {"xml:id": 'side_'+attribute} + sideAttributeValues = [] + @rectos.each do |rectoID, recto| + if not sideAttributeValues.include? recto[attribute] and recto[attribute] != "None" + sideAttributeValues.push(recto[attribute]) + end + end + @versos.each do |versoID, verso| + if 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| + termID = {"xml:id": "side_"+attribute+"_"+attributeValue.parameterize.underscore} + xml.term termID do + xml.text attributeValue + 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 @@ -177,29 +425,29 @@ def buildDotModel(project) idPrefix = project.shelfmark.parameterize.underscore xml.quires do @groupIDs.each_with_index do |groupID, index| - group = project.groups.find(groupID) - getLeafMemberIDs(group.memberIDs, project) + group = @groups[groupID] parents = parentsOrders(groupID, project) - groupMemberOrder = parents.pop - idPostfix = parents.empty? ? groupMemberOrder.to_s : parents.join("-")+"-"+groupMemberOrder.to_s + 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-"+(@groupIDs.index(group.parentID)+1).to_s + quireAttributes[:parent] = idPrefix+"-q-"+parents.join("-") end xml.quire quireAttributes do xml.text index + 1 end - @groups[groupID] = quireAttributes["xml:id"] + @groups[groupID]["xmlID"] = quireAttributes["xml:id"] end end @leafIDs.each_with_index do |leafID, index| leaf = project.leafs.find(leafID) parents = parentsOrders(leafID, project) - leafMemberOrder = parents.pop - idPostfix = parents.join("-")+"-"+leafMemberOrder.to_s + leafemberOrder = parents.pop + idPostfix = parents.join("-")+"-"+leafemberOrder.to_s leafAttributes = {} leafAttributes["xml:id"] = idPrefix+"-"+idPostfix leafAttributes["stub"] = "yes" if leaf.stubType != "None" @@ -212,15 +460,15 @@ def buildDotModel(project) end mode = {} - if leaf.type != "None" + 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] = leafMemberOrder - qAttributes[:leafno] = leafMemberOrder + 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] @@ -235,27 +483,9 @@ def buildDotModel(project) xml.single :val => "yes" end - if leaf.attached_above != "None" or leaf.attached_below != "None" - targetLeafAbove = parents.join("-")+"-"+(leafMemberOrder.to_i-1).to_s - targetLeafBelow = parents.join("-")+"-"+(leafMemberOrder.to_i+1).to_s - if leaf.attached_above != "None" and leaf.attached_below != "None" - xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafAbove+" #"+targetLeafBelow, :type => leaf.attached_above) do - xml.text leaf.attached_above + "_To_Above_and_Below" - end - elsif leaf.attached_above != "None" - xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafAbove, :type => leaf.attached_above) do - xml.text leaf.attached_above + "_To_Above" - end - elsif leaf.attached_below != "None" - xml.send("attachment-method", :certainty => 1, :target => "#"+targetLeafBelow, :type => leaf.attached_below) do - xml.text leaf.attached_below + "_To_Below" - end - end - end - rectoSide = project.sides.find(leaf.rectoID) rectoAttributes = {} - rectoAttributes["xml:id"] = leafAttributes["xml:id"]+"-R" + rectoAttributes["xml:id"] = leafAttributes["xml:id"] rectoAttributes[:type] = "Recto" if rectoSide.folio_number rectoAttributes[:folioNumber] = rectoSide.folio_number @@ -268,10 +498,11 @@ def buildDotModel(project) 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"]+"-V" + versoAttributes["xml:id"] = leafAttributes["xml:id"] versoAttributes[:type] = "Verso" if versoSide.folio_number versoAttributes[:folioNumber] = versoSide.folio_number @@ -284,57 +515,185 @@ def buildDotModel(project) 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 - project.notes.each_with_index do |note, index| - noteAttributes = {} - noteAttributes["xml:id"] = idPrefix+"-n-"+(index+1).to_s - noteAttributes[:type] = note.type - linkedObjectIDs = [] - note.objects["Group"].each do |groupID| - linkedObjectIDs.push("#"+@groups[groupID]) + # 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 - note.objects["Leaf"].each do |leafID| - linkedObjectIDs.push("#"+@leafs[leafID]["xmlID"]) + 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'].each do |attribute| + attributeValue = group[attribute] + if @allGroupAttributeValues.include? attributeValue + linkedAttributes.push("group_"+attribute+"_"+attributeValue.parameterize.underscore) + end end - note.objects["Recto"].each do |rectoID| - linkedObjectIDs.push("#"+@rectos[rectoID]["xml:id"]) + ['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 - note.objects["Verso"].each do |versoID| - linkedObjectIDs.push("#"+@versos[versoID]["xml:id"]) + 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 - noteAttributes[:target] = linkedObjectIDs.join(" ") - xml.note noteAttributes do - xml.text note.title + ": " + note.description + 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 - - # @rectos.each do |rectoID, rectoAttributes| - # noteAttributes = {} - # noteAttributes["xml:id"] = rectoAttributes["xml:id"] - # noteAttributes[:target] = rectoAttributes[:target] - # noteAttributes[:type] = "Recto" - # # noteAttributes[:texture] = rectoAttributes[:texture] - # xml.note noteAttributes do - # xml.text rectoAttributes[:folioNumber] - # end - # end - - end - - xml.mapping do + # Map rectos to attributes and notes and sides @rectos.each do |rectoID, attributes| - if attributes[:image] - mapAttributes = {} - mapAttributes[:side] = attributes["xml:id"] - mapAttributes[:target] = attributes[:image] - xml.map mapAttributes do - termAttributes = {} - termAttributes[:target] = attributes[:image] - xml.term termAttributes + 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'].each do |attribute| + attributeValue = recto[attribute] + if @allSideAttributeValues.include? attributeValue + linkedAttributes.push("side_"+attribute+"_"+attributeValue.parameterize.underscore) + 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'].each do |attribute| + attributeValue = verso[attribute] + if @allSideAttributeValues.include? attributeValue + linkedAttributes.push("side_"+attribute+"_"+attributeValue.parameterize.underscore) + 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 @@ -344,21 +703,29 @@ def buildDotModel(project) end - # Populate leaf orders recursively - def getLeafMemberIDs(memberIDs, project, leafMember=1) + # 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" - getLeafMemberIDs(project.groups.find(memberID).memberIDs, project, leafMember) + @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] = {"memberOrder": leafMember} + @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 = [] diff --git a/viscoll-api/app/helpers/controller_helper/filter_helper.rb b/viscoll-api/app/helpers/controller_helper/filter_helper.rb index 7433e997..4017a366 100644 --- a/viscoll-api/app/helpers/controller_helper/filter_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/filter_helper.rb @@ -9,81 +9,23 @@ def runValidations(queries) end queries.each_with_index do |query, index| error = {type: "", attribute: "", condition: "", values: "", conjunction: ""} - case query["type"] - when "group" - if !["type", "title"].include?(query["attribute"]) - error["attribute"] = "valid attributes for group: [type, title]" - haveErrors = true - end - case query["attribute"] - when "type" - if !["equals", "not equals"].include?(query["condition"]) - error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals]" - haveErrors = true - end - when "title" - if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) - error["condition"] = "valid conditions for group attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" - haveErrors = true - end - end - when "leaf" - if !["type", "material", "conjoined_leaf_order", "attached_above", "attached_below", "stub"].include?(query["attribute"]) - error["attribute"] = "valid attributes for leaf: [type, material, conjoined_leaf_order, attached_above, attached_below, stub]" - haveErrors = true - end - case query["attribute"] - when "type", "material", "conjoined_to", "attached_to", "stub" - if !["equals", "not equals"].include?(query["condition"]) - error["condition"] = "valid conditions for leaf attribute "+query["attribute"]+" : [equals, not equals]" - haveErrors = true - end - end - when "side" - if !["folio_number", "texture", "script_direction", "uri"].include?(query["attribute"]) - error["attribute"] = "valid attributes for side: [folio_number, texture, script_direction, uri]" - haveErrors = true - end - case query["attribute"] - when "texture", "script_direction" - if !["equals", "not equals"].include?(query["condition"]) - error["condition"] = "valid conditions for side attribute "+query["attribute"]+" : [equals, not equals]" - haveErrors = true - end - when "folio_number", "uri" - if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) - error["condition"] = "valid conditions for side attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" - haveErrors = true - end - end - when "note" - if !["title", "type", "description"].include?(query["attribute"]) - error["attribute"] = "valid attributes for note: [title, type, description]" - haveErrors = true - end - case query["attribute"] - when "type" - if !["equals", "not equals"].include?(query["condition"]) - error["condition"] = "valid conditions for note attribute "+query["attribute"]+" : [equals, not equals]" - haveErrors = true - end - when "title", "description" - if !["equals", "not equals", "contains", "not contains"].include?(query["condition"]) - error["condition"] = "valid conditions for note attribute "+query["attribute"]+" : [equals, not equals, contains, not contains]" - haveErrors = true - end - end - else - error["type"] = "type should be one of: [group, leaf, side, note]" + 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 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'], + '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 index 7762cb5f..57c02e36 100644 --- a/viscoll-api/app/helpers/controller_helper/groups_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/groups_helper.rb @@ -2,22 +2,48 @@ module ControllerHelper module GroupsHelper include ControllerHelper::LeafsHelper - def addLeavesInside(project_id, group, noOfLeafs, conjoin, oddMemberLeftOut) - newlyAddedLeafs = [] - newlyAddedLeafIDs = [] - noOfLeafs.times do |i| - leaf = Leaf.new({project_id: project_id, parentID:group.id.to_s, nestLevel: group.nestLevel}) - leaf.save() - newlyAddedLeafs.push(leaf) - newlyAddedLeafIDs.push(leaf.id.to_s) - 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) + 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_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..04e0a6c0 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_mapping_helper.rb @@ -0,0 +1,91 @@ +require 'zip' + +module ControllerHelper + module ImportMappingHelper + + def handleMappingImport(newProject, imageData, current_user) + begin + uploadedImages = {} + if imageData!="" + zipFile = Paperclip.io_adapters.for(imageData) + Zip::File.open(zipFile.path) do |zip_file| + zip_file.each do |file| + # Go through each file and check if it exists in the user directory and link them. + # If it doesn't exist, create a new Image and link it to newProject and its Side. + tempfile = Tempfile.new([File.basename(file.name).split("_", 2)[1].split('.', 2)[0], File.basename(file.name).split("_", 2)[1].split('.', 2)[1]]) + tempfile.binmode + tempfile.write file.get_input_stream.read + tempfile.rewind + imageID = File.basename(file.name).split("_", 2)[0] + filename = File.basename(file.name).split("_", 2)[1] + newImage = Image.new(user: current_user, filename: filename, image: tempfile, projectIDs: [newProject.id.to_s]) + uploadedImages[filename] = {:image => newImage, :file => file} + 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 + # No Image exists in the current_user direcroty. + # Check if any Image with 'filename' was uploaded during import. + if uploadedImages.key?(filename) + newImage = uploadedImages[filename][:image] + # Check if uploaded Image Filename already exists in the current_user directory + existingImage = current_user.images.where(:filename => filename).first + if existingImage + # Check if this new Image is different from the existing Image + if newImage.image_fingerprint==existingImage.image_fingerprint + # Same Image. So Link this Image to the this 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. + newFilename = "#{newImage.filename.split('.', 2)[0]}(copy).#{newImage.filename.split('.', 2)[1]}" + tempfile = Tempfile.new([newFilename.split(".", 2)[0], newFilename.split(".", 2)[1]]) + tempfile.binmode + tempfile.write uploadedImages[filename][:file].get_input_stream.read + tempfile.rewind + newImage = Image.new(user: current_user, filename: newFilename, image: tempfile, 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 + # New Image + 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..5a976780 --- /dev/null +++ b/viscoll-api/app/helpers/controller_helper/import_xml_helper.rb @@ -0,0 +1,378 @@ +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") + @projectInformation[:title] = projectTitleNode.text + 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: "", + 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_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["stube"] ? "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, + attachment_method: "None", + 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, + texture: "None", + image: {}, + script_direction: "None" + }, + parentOrder: leafOrder, + noteTitles: [] + } + @versos[leafOrder] = { + params: { + folio_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 (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=="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=="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 \ No newline at end of file diff --git a/viscoll-api/app/helpers/controller_helper/leafs_helper.rb b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb index b52e3a12..7289cc87 100644 --- a/viscoll-api/app/helpers/controller_helper/leafs_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/leafs_helper.rb @@ -3,31 +3,29 @@ module LeafsHelper # Auto-Conjoin the given leaves def autoConjoinLeaves(leaves, oddLeafNumber) + leafIds = leaves.collect { |leaf| leaf.id.to_s } if leaves.size.odd? - oddLeaf = leaves[oddLeafNumber] - if (oddLeaf.conjoined_to) + oddLeaf = leaves[oddLeafNumber-1] + unless oddLeaf.conjoined_to.blank? @project.leafs.find(oddLeaf.conjoined_to).update(conjoined_to: nil) oddLeaf.update(conjoined_to: nil) end leaves.delete_at(oddLeafNumber-1) + leafIds.delete_at(oddLeafNumber-1) end leaves.each do |leaf| - if leaf.conjoined_to + 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 - leaves.size.times do |i| - if (leaves.size/2 == i) - break - else - leafOne = leaves[i] - leafTwo = leaves[leaves.size-i-1] - leafOne.update(conjoined_to: leafTwo.id.to_s) - leafTwo.update(conjoined_to: leafOne.id.to_s) - end + (leaves.size/2).times do |i| + leafOne = leaves[i] + leafTwo = leaves[-i-1] + leafOne.update(conjoined_to: leafTwo.id.to_s) + leafTwo.update(conjoined_to: leafOne.id.to_s) end end diff --git a/viscoll-api/app/helpers/controller_helper/projects_helper.rb b/viscoll-api/app/helpers/controller_helper/projects_helper.rb index 32ff55fb..a993bcf6 100644 --- a/viscoll-api/app/helpers/controller_helper/projects_helper.rb +++ b/viscoll-api/app/helpers/controller_helper/projects_helper.rb @@ -29,12 +29,15 @@ def addGroupsLeafsConjoin(project, allGroups) project.add_groupIDs(groupIDs, 0) end - def getManifestInformation(url) images = [] - 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"]}) + 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 @@ -63,21 +66,35 @@ def generateResponse() } @project.manifests.each do |manifestID, manifest| manifestInformation = getManifestInformation(manifest[:url]) - manifestName = manifestInformation[:name] + 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" + # } - rootMemberOrder = 1 @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, - "order": index + 1, "type": group.type, "title": group.title, "tacketed": group.tacketed, @@ -87,11 +104,7 @@ def generateResponse() "notes": [], "memberIDs": group.memberIDs, "memberType": "Group", - "memberOrder": group.parentID ? nil : rootMemberOrder } - if group.nestLevel == 1 - rootMemberOrder += 1 - end end @groups.each do | groupID, group | if group[:nestLevel] == 1 @@ -101,12 +114,10 @@ def generateResponse() @project.leafs.each do | leaf | @leafs[leaf.id.to_s] = { "id": leaf.id.to_s, - "order": @leafIDs.index(leaf.id.to_s) + 1, "material": leaf.material, "type": leaf.type, "attachment_method": leaf.attachment_method, "conjoined_to": leaf.conjoined_to, - "conjoined_leaf_order": leaf.conjoined_to ? @leafIDs.index(leaf.conjoined_to) + 1 : nil, "attached_above": leaf.attached_above, "attached_below": leaf.attached_below, "stub": leaf.stub, @@ -116,7 +127,6 @@ def generateResponse() "versoID": leaf.versoID, "notes": [], "memberType": "Leaf", - "memberOrder": @leafs[leaf.id.to_s][:memberOrder] } end @@ -127,7 +137,6 @@ def generateResponse() obj = { "id": side.id.to_s, "parentID": side.parentID, - "parentOrder": parentOrder, "folio_number": side.folio_number, "texture": side.texture, "image": side.image, @@ -143,35 +152,10 @@ def generateResponse() end # Generate list of recto and verso ID's - # Generate folio numbers for sides that do not have folio numbers parentOrder.to_s + side.id[0] - endleafCount = 0 - folioNumberCount = 0 @leafIDs.each do | leafID | leaf = @leafs[leafID] @rectoIDs.push(leaf[:rectoID]) @versoIDs.push(leaf[:versoID]) - recto = @rectos[leaf[:rectoID]] - verso = @versos[leaf[:versoID]] - if leaf[:type] == "Endleaf" - endleafCount += 1 - if recto[:folio_number] == nil - recto[:folio_number] = to_roman(endleafCount) + recto[:id][0] - end - if verso[:folio_number] == nil - verso[:folio_number] = to_roman(endleafCount) + verso[:id][0] - end - else - if (recto[:folio_number] == nil) || (verso[:folio_number] == nil) - folioNumberCount += 1 - end - if recto[:folio_number] == nil - recto[:folio_number] = (folioNumberCount).to_s + recto[:id][0] - end - if verso[:folio_number] == nil - verso[:folio_number] = (folioNumberCount).to_s + verso[:id][0] - end - end - end @project.notes.each do | note | @@ -216,10 +200,8 @@ def getLeafMembers(memberIDs) memberIDs.each_with_index do | memberID, index | if memberID[0] == "G" getLeafMembers(@groups[memberID][:memberIDs]) - @groups[memberID][:memberOrder] = index + 1 elsif memberID[0] == "L" @leafIDs.push(memberID) - @leafs[memberID] = {"memberOrder": index + 1} end end end diff --git a/viscoll-api/app/models/group.rb b/viscoll-api/app/models/group.rb index 2f2580b7..702689b1 100644 --- a/viscoll-api/app/models/group.rb +++ b/viscoll-api/app/models/group.rb @@ -4,7 +4,7 @@ class Group # Fields field :title, type: String, default: "None" - field :type, type: String, default: "None" + field :type, type: String, default: "Quire" field :tacketed, type: Array, default: [] field :sewing, type: Array, default: [] field :nestLevel, type: Integer, default: 1 diff --git a/viscoll-api/app/models/image.rb b/viscoll-api/app/models/image.rb new file mode 100644 index 00000000..7bc6e23d --- /dev/null +++ b/viscoll-api/app/models/image.rb @@ -0,0 +1,40 @@ +class Image + include Mongoid::Document + include Mongoid::Paperclip + + # Fields + has_mongoid_attached_file :image + field :filename, type: String + 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 + + validates_attachment :image, content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] } + do_not_validate_attachment_file_type :image + 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 + + Paperclip.interpolates :userID do |attachment, style| + attachment.instance.user.id.to_s + end + + Paperclip.interpolates :extension do |attachment, style| + attachment.instance.image_content_type.split("/")[-1] + end + +end diff --git a/viscoll-api/app/models/leaf.rb b/viscoll-api/app/models/leaf.rb index 7e03ea01..ee9fe325 100644 --- a/viscoll-api/app/models/leaf.rb +++ b/viscoll-api/app/models/leaf.rb @@ -21,7 +21,7 @@ class Leaf # Callbacks before_create :edit_ID, :create_sides - before_destroy :unlink_notes, :destroy_sides + before_destroy :unlink_notes, :destroy_sides, :update_parent_group # Remove itself from its parent group @@ -74,5 +74,13 @@ def update_attached_to 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/project.rb b/viscoll-api/app/models/project.rb index ea3fb0ab..54bcdc0f 100644 --- a/viscoll-api/app/models/project.rb +++ b/viscoll-api/app/models/project.rb @@ -6,7 +6,7 @@ class Project 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, name: "", url: ""} } + 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: [] @@ -17,6 +17,9 @@ class Project 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." @@ -36,4 +39,19 @@ def remove_groupID(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 index a333484e..a3f4723d 100644 --- a/viscoll-api/app/models/side.rb +++ b/viscoll-api/app/models/side.rb @@ -14,7 +14,7 @@ class Side has_and_belongs_to_many :notes, inverse_of: nil # Callbacks - before_destroy :unlink_notes + before_destroy :unlink_notes, :unlink_image protected # If linked to note(s), remove link from the note(s)'s side @@ -25,4 +25,14 @@ def unlink_notes 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 index 6fc9f0aa..4b2df830 100644 --- a/viscoll-api/app/models/user.rb +++ b/viscoll-api/app/models/user.rb @@ -6,7 +6,8 @@ class User 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/exports/show.json.jbuilder b/viscoll-api/app/views/exports/show.json.jbuilder index 700404bd..ecaa5533 100644 --- a/viscoll-api/app/views/exports/show.json.jbuilder +++ b/viscoll-api/app/views/exports/show.json.jbuilder @@ -1,6 +1,16 @@ -json.project @data[:project] -json.Groups @data[:groups] -json.Leafs @data[:leafs] -json.Rectos @data[:rectos] -json.Versos @data[:versos] -json.Notes @data[:notes] \ No newline at end of file +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/projects/index.json.jbuilder b/viscoll-api/app/views/projects/index.json.jbuilder index 69fbb12f..0aebab9d 100644 --- a/viscoll-api/app/views/projects/index.json.jbuilder +++ b/viscoll-api/app/views/projects/index.json.jbuilder @@ -1,3 +1,13 @@ -json.array!(@projects.desc(:updated_at)) do | project | - json.extract! project, :id, :title, :shelfmark, :metadata, :created_at, :updated_at +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 index fd6e2fd3..690a9c3c 100644 --- a/viscoll-api/app/views/projects/show.json.jbuilder +++ b/viscoll-api/app/views/projects/show.json.jbuilder @@ -1,4 +1,18 @@ -json.merge! @data[:project] +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] diff --git a/viscoll-api/config/application.rb b/viscoll-api/config/application.rb index e0340619..017ceb00 100644 --- a/viscoll-api/config/application.rb +++ b/viscoll-api/config/application.rb @@ -27,8 +27,8 @@ class Application < Rails::Application # Skip views, helpers and assets when generating a new resource. config.api_only = true - # Mongo::Logger.logger.level = Logger::FATAL - # config.log_level = :warn + 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 diff --git a/viscoll-api/config/environments/development.rb b/viscoll-api/config/environments/development.rb index 4561e32a..67d11c73 100644 --- a/viscoll-api/config/environments/development.rb +++ b/viscoll-api/config/environments/development.rb @@ -44,4 +44,9 @@ # 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 + + # Configure Paperclip to access ImageMagic directory + Paperclip.options[:command_path] = "/usr/bin/" + # Redirect uploaded files to uploads + Paperclip::Attachment.default_options[:path] = ":rails_root/uploads/:class/:userID/:basename.:extension" end diff --git a/viscoll-api/config/environments/test.rb b/viscoll-api/config/environments/test.rb index 7c76952d..51035f09 100644 --- a/viscoll-api/config/environments/test.rb +++ b/viscoll-api/config/environments/test.rb @@ -40,4 +40,10 @@ # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + + # Don't show Paperclip logs + Paperclip.options[:log] = false + + # Redirect uploaded files to uploads/test-images + Paperclip::Attachment.default_options[:path] = ":rails_root/uploads/test-files/:class/:id_partition/:style.:extension" end diff --git a/viscoll-api/config/mongoid.yml b/viscoll-api/config/mongoid.yml index cdce943a..f5dfbc10 100644 --- a/viscoll-api/config/mongoid.yml +++ b/viscoll-api/config/mongoid.yml @@ -148,9 +148,7 @@ development: test: clients: default: - database: viscoll_api_test - hosts: - - localhost:27017 + uri: mongodb://localhost:27017/viscoll_test options: read: mode: :primary diff --git a/viscoll-api/config/routes.rb b/viscoll-api/config/routes.rb index b8be2adf..e7391bca 100644 --- a/viscoll-api/config/routes.rb +++ b/viscoll-api/config/routes.rb @@ -13,20 +13,30 @@ post '/feedback', to: 'feedback#create', defaults: {format: :json} # PROJECT ENDPOINTS - put '/projects/:id/filter', to: 'filter#show', defaults: {format: :json}, only: [:show] - get '/projects/:id/export/:format', to: 'export#show', defaults: {format: :json}, only: [:show] - put '/projects/import', to: 'import#index', defaults: {format: :json}, only: [:index] - post '/projects/:id/manifests', to: 'projects#createManifest', defaults: {format: :json}, only: [:create] - put '/projects/:id/manifests', to: 'projects#updateManifest', defaults: {format: :json}, only: [:update] - delete '/projects/:id/manifests', to: 'projects#deleteManifest', defaults: {format: :json}, only: [:destroy] + 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} 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/generateFolio', to: 'leafs#generateFolio', defaults: {format: :json}, only: [:update] 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] diff --git a/viscoll-api/config/secrets.yml b/viscoll-api/config/secrets.yml index 96ae2160..e0f98441 100644 --- a/viscoll-api/config/secrets.yml +++ b/viscoll-api/config/secrets.yml @@ -13,6 +13,8 @@ development: secret_key_base: 98eb3bcbe406c141ad93c58e5f5ff08ab7348c82d688b78ee1fb1a30559d7104081e0dd9bf97c8e080a54f1d408f7f9d22710439c44cbbddc332861994b1c531 admin_email: 'smtp://localhost:1025' + api_url: 'http://localhost:3001' + test: secret_key_base: a8986cd44e89b6547fe0c8ebd320706dc2dbd6aa617e42c5625b9420fa8ab8347beeedb0690138e178e79583b0f7c71b45fac99409d96653030c6a94c14d9d9d diff --git a/viscoll-api/public/viscoll-datamodel2.rng b/viscoll-api/public/viscoll-datamodel2.rng index ab405180..4fd9ad77 100644 --- a/viscoll-api/public/viscoll-datamodel2.rng +++ b/viscoll-api/public/viscoll-datamodel2.rng @@ -195,23 +195,26 @@ - - - - - - certainty - - - - - id - - - - + + + + + + certainty + + + + + id + + + + + + + mapping diff --git a/viscoll-api/spec/factories/groups.rb b/viscoll-api/spec/factories/groups.rb index de75a9a1..8eed6302 100644 --- a/viscoll-api/spec/factories/groups.rb +++ b/viscoll-api/spec/factories/groups.rb @@ -5,20 +5,69 @@ sequence :booklet_title do |n| "Booklet #{n}" end + sequence :group_title do |n| + "Group #{n}" + end factory :group, class: Group do - transient do - groupOrder nil + 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 { groupOrder ? "Group #{groupOrder}" : generate(:quire_title) } + 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, class: Group do + + 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..2bd93a33 --- /dev/null +++ b/viscoll-api/spec/factories/images.rb @@ -0,0 +1,25 @@ +FactoryGirl.define do + sequence :image_filename do |n| + "Image #{n}" + end + + sequence :image_original_filename do |n| + "image_#{n}" + end + + factory :image do + filename { generate(:image_filename) } + + factory :pixel do + image { File.new(File.dirname(__FILE__) + '/../fixtures/pixel.png', 'rb') } + end + + factory :shiba_inu do + image { File.new(File.dirname(__FILE__) + '/../fixtures/shibainu.jpg', 'rb') } + end + + factory :viscoll_logo do + image { File.new(File.dirname(__FILE__) + '/../fixtures/viscoll.png', 'rb') } + end + end +end diff --git a/viscoll-api/spec/factories/leafs.rb b/viscoll-api/spec/factories/leafs.rb index 64c1f3a9..c56245d9 100644 --- a/viscoll-api/spec/factories/leafs.rb +++ b/viscoll-api/spec/factories/leafs.rb @@ -1,7 +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 index e864c2c0..2f5e6869 100644 --- a/viscoll-api/spec/factories/notes.rb +++ b/viscoll-api/spec/factories/notes.rb @@ -2,8 +2,33 @@ sequence :note_title do |n| "Note #{n}" end - factory :note do + 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 \ No newline at end of file +end diff --git a/viscoll-api/spec/factories/projects.rb b/viscoll-api/spec/factories/projects.rb index 71a8421f..1c30e681 100644 --- a/viscoll-api/spec/factories/projects.rb +++ b/viscoll-api/spec/factories/projects.rb @@ -3,6 +3,15 @@ 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) } @@ -10,34 +19,62 @@ 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 - - -# include ActionDispatch::TestProcess -# FactoryGirl.define do -# sequence :shelfmark do |n| -# "Cod. UTL #{n}" -# end - -# sequence :uri do |n| -# "http://www.example.org/#{n}" -# end - -# factory :empty_manuscript, class: Manuscript do -# shelfmark { generate :shelfmark } -# uri "" -# date { Date.today } -# end - -# factory :manuscript do -# shelfmark { generate :shelfmark } -# uri { generate :uri } -# date { Date.today } -# after(:create) do |m| -# m.leafs << FactoryGirl.create(:leaf, manuscript: m) -# end -# end -# end \ 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 0000000000000000000000000000000000000000..f429e93bdc6602aa1df17ddbaaf87167c20333a7 GIT binary patch literal 781 zcmWIWW@Zs#U|`^2I2zjPoiFp}u04?F2E_6VG7P4PMixd!hN;PhmX;P~2C3$WMro#L zDTeWeL3#yw>7gN<49rg6hEX6~TEWf0$nt`jfdNds=J`3>lex}R|^O4eaqSZ-GP(MKiqzLsUN2=LjZa1G8&}r zG7O`6crh}`G2@C!31ECez?Mc3O)y?rA@Pb9rvcu?S~3HlC9p`M)Di + + + + 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 0000000000000000000000000000000000000000..c958052161e90c13de786d3ff9f7fa9d6e371a4b GIT binary patch literal 128103 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 z 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�VK2KqMt(jHf=si|-~s0QD+OQ^MgJyz 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 '*/*', '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.' } } + '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) + @testgroup = FactoryGirl.create(:group, project: @project, nestLevel: 1, title: 'Group 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) + @testmidgroup = FactoryGirl.create(:group, project: @project, parentID: @testgroup.id.to_s, nestLevel: 2, title: 'Group 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') @@ -32,11 +32,11 @@ 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'] + noteTypes: ['Ink', 'Unknown'] }) expect(result[:groups]).to eq({ - 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"]} + 1 => {:params=>{:type=>"Quire", :title=>"Group 1", :nestLevel=>1}, :tacketed=>[], :sewing=>[], :parentOrder=>nil, :memberOrders=>["Leaf_1", "Leaf_2", "Group_2", "Leaf_5", "Leaf_6"]}, + 2 => {:params=>{:type=>"Quire", :title=>"Group 2", :nestLevel=>2}, :tacketed=>[], :sewing=>[], :parentOrder=>1, :memberOrders=>["Leaf_3", "Leaf_4"]} }) expect(result[:leafs]).to eq({ 1 => {:params=>{:material=>"Paper", :type=>"Original", :attachment_method=>"None", :attached_above=>"None", :attached_below=>"None", :stub=>"None", :nestLevel=>1}, :conjoined_leaf_order=>nil, :parentOrder=>1, :rectoOrder=>1, :versoOrder=>1}, @@ -66,4 +66,64 @@ 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("taxonomy[xml|id='side_texture'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['side_texture_hair', 'Hair'], + ['side_texture_flesh', 'Flesh'] + ) + 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_texture_hair'], + ['#ravenna_384_2339-1-2', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-2-3', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-2-4', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-3', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-4', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-1', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2-3', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2-4', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-3', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-4', 'verso', '#side_texture_flesh'] + ) + 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 index d0c65f31..23c25c14 100644 --- a/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/filter_helper_spec.rb @@ -73,11 +73,11 @@ 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_leaf_order, attached_above, attached_below, stub]') + 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', 'attached_to', 'stub'].each do |attribute| + ['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'] } ]) 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..e5742c50 --- /dev/null +++ b/viscoll-api/spec/helpers/controller_helper/import_mapping_helper_spec.rb @@ -0,0 +1,61 @@ +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 + + describe 'handleMappingImport' do + it 'should run properly with images in various attachment situations' do + # Prep user with preloaded images + preloads = [ + FactoryGirl.create(:pixel, user: @user, projectIDs: [@project.id.to_s], filename: '1V.png'), + FactoryGirl.create(:shiba_inu, user: @user, projectIDs: [@project.id.to_s], filename: '2R.png', id: '5a28221ec199860e7a2f5fd1shibainu'), + FactoryGirl.create(:viscoll_logo, user: @user, projectIDs: [@project.id.to_s], filename: '2V.png', 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/5a28221ec199860e7a2f5fd1pixel_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' + }) + handleMappingImport(@project, File.new(File.dirname(__FILE__) + '/../../fixtures/dots_exported.zip', 'rb'), @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 index fff31e21..9ed42ed6 100644 --- a/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/leafs_helper_spec.rb @@ -75,6 +75,28 @@ 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 diff --git a/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb index fb9cf038..a511d371 100644 --- a/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb +++ b/viscoll-api/spec/helpers/controller_helper/projects_helper_spec.rb @@ -72,7 +72,7 @@ 'manifests': { '12341234' => { 'id' => '12341234', 'url' => 'https://digital.library.villanova.edu/Item/vudl:99213/Manifest', - 'name' => 'Boston, and Bunker Hill. Provided by Villanova ...', + 'name' => 'Boston, and Bunker Hill.', 'images' => [ { 'label' => nil, 'url' => 'https://iiif.library.villanova.edu/image/vudl%3A99215', 'manifestID' => '12341234' } ] } }, }) diff --git a/viscoll-api/spec/models/group_spec.rb b/viscoll-api/spec/models/group_spec.rb index d7e6ccff..8ce7f7b5 100644 --- a/viscoll-api/spec/models/group_spec.rb +++ b/viscoll-api/spec/models/group_spec.rb @@ -94,6 +94,7 @@ 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) diff --git a/viscoll-api/spec/models/image_spec.rb b/viscoll-api/spec/models/image_spec.rb new file mode 100644 index 00000000..8bd5427b --- /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(:pixel, 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(:shiba_inu, 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: 'pixel.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 index 49163ab7..3a5402b2 100644 --- a/viscoll-api/spec/models/leaf_spec.rb +++ b/viscoll-api/spec/models/leaf_spec.rb @@ -21,6 +21,10 @@ 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 diff --git a/viscoll-api/spec/models/project_spec.rb b/viscoll-api/spec/models/project_spec.rb index cf8e7fbd..e0d3b2d1 100644 --- a/viscoll-api/spec/models/project_spec.rb +++ b/viscoll-api/spec/models/project_spec.rb @@ -60,4 +60,19 @@ 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 index 13df31c8..71e92ad3 100644 --- a/viscoll-api/spec/models/side_spec.rb +++ b/viscoll-api/spec/models/side_spec.rb @@ -13,7 +13,8 @@ it { is_expected.to have_and_belong_to_many(:notes) } before :each do - @project = FactoryGirl.create(:project) + @user = FactoryGirl.create(:user) + @project = FactoryGirl.create(:project, user: @user) @leaf = FactoryGirl.create(:leaf, project: @project) @side = Side.find(id: @leaf.rectoID) end @@ -30,5 +31,13 @@ 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/viscoll-api/spec/requests/groups/groups_create_spec.rb b/viscoll-api/spec/requests/groups/groups_create_spec.rb index fe5c6716..540fc795 100644 --- a/viscoll-api/spec/requests/groups/groups_create_spec.rb +++ b/viscoll-api/spec/requests/groups/groups_create_spec.rb @@ -31,11 +31,10 @@ context 'and standard group' do before do post '/groups', 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) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'adds a group to the project' do @@ -50,12 +49,11 @@ @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'} - @body = JSON.parse(response.body) @group2.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'adds a group to the project' do diff --git a/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb b/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb index 3c1cdaf4..baf0263f 100644 --- a/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb +++ b/viscoll-api/spec/requests/groups/groups_destroy_multiple_spec.rb @@ -37,11 +37,10 @@ before do delete '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} @project.reload - @body = JSON.parse(response.body) end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'deletes only the specified groups' do @@ -57,12 +56,11 @@ before do @parameters[:groups][0] += 'missing' @parameters[:groups][1] += 'missing' - put "/groups", params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} - @body = JSON.parse(response.body) + delete "/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) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'leaves the groups alone' do @@ -99,7 +97,6 @@ context 'with corrupted authorization' do before do delete '/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 diff --git a/viscoll-api/spec/requests/groups/groups_destroy_spec.rb b/viscoll-api/spec/requests/groups/groups_destroy_spec.rb index cb8cfe80..75854507 100644 --- a/viscoll-api/spec/requests/groups/groups_destroy_spec.rb +++ b/viscoll-api/spec/requests/groups/groups_destroy_spec.rb @@ -33,12 +33,11 @@ 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'} - @body = JSON.parse(response.body) @project.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'destroys the group' do diff --git a/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb b/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb index a24b64cf..454f0a3e 100644 --- a/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb +++ b/viscoll-api/spec/requests/groups/groups_update_multiple_spec.rb @@ -42,11 +42,10 @@ before do put '/groups', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} @project.reload - @body = JSON.parse(response.body) end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'edits the group' do diff --git a/viscoll-api/spec/requests/groups/groups_update_spec.rb b/viscoll-api/spec/requests/groups/groups_update_spec.rb index 296ed440..a92e782b 100644 --- a/viscoll-api/spec/requests/groups/groups_update_spec.rb +++ b/viscoll-api/spec/requests/groups/groups_update_spec.rb @@ -30,12 +30,11 @@ 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'} - @body = JSON.parse(response.body) @group.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'edits the group' do 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..5943b933 --- /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(:pixel, user: @user) + @image2 = FactoryGirl.create(:pixel, 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..6e7344de --- /dev/null +++ b/viscoll-api/spec/requests/images/show_images_spec.rb @@ -0,0 +1,64 @@ +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 + + 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(File.dirname(__FILE__) + '/../../fixtures/pixel.png', '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..f416eb1c --- /dev/null +++ b/viscoll-api/spec/requests/images/upload_images_spec.rb @@ -0,0 +1,174 @@ +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 + + 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(image_file_name: 'green')).to exist + expect(Image.where(image_file_name: 'blue')).to exist + expect(Image.find_by(image_file_name: 'green').projectIDs).to include @project.id.to_s + expect(Image.find_by(image_file_name: 'blue').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(image_file_name: 'green')).to exist + expect(Image.where(image_file_name: 'green_copy(1)')).to exist + expect(Image.find_by(image_file_name: 'green').projectIDs).to include @project.id.to_s + expect(Image.find_by(image_file_name: 'green_copy(1)').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..f5de12e2 --- /dev/null +++ b/viscoll-api/spec/requests/images/zip_images_spec.rb @@ -0,0 +1,64 @@ +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'] + 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("#{@image1.image.path.split('/')[0..-2].join('/')}/#{@project.id}_images.zip", 'w+') { |file| file.write('testcontent') } + get "/images/zip/#{@image1.id}_#{@project.id}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json'} + end + after do + File.delete("#{@image1.image.path.split('/')[0..-2].join('/')}/#{@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 eq "Exception" + 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 index 1596f6e8..66ed6119 100644 --- a/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_conjoin_spec.rb @@ -24,14 +24,13 @@ 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'} - @body = JSON.parse(response.body) @project.reload @group.reload @leafs.each { |leaf| leaf.reload } end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'updates the affected leafs' do @@ -46,18 +45,16 @@ 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'} - @body = JSON.parse(response.body) @project.reload @group.reload @leafs.each { |leaf| leaf.reload } end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'updates the affected leafs' do - pending 'BUG: Should conjoin first-fifth, second-fourth and leave the third alone' 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 @@ -66,6 +63,33 @@ 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] diff --git a/viscoll-api/spec/requests/leafs/leafs_create_spec.rb b/viscoll-api/spec/requests/leafs/leafs_create_spec.rb index a0104621..69974d45 100644 --- a/viscoll-api/spec/requests/leafs/leafs_create_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_create_spec.rb @@ -36,13 +36,12 @@ context 'and standard leaf' do before do post '/leafs', params: @parameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} - @body = JSON.parse(response.body) @project.reload @group.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'adds 5 leafs to the project and group' do diff --git a/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb b/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb index 652a7803..7170447a 100644 --- a/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_multiple_spec.rb @@ -53,7 +53,6 @@ context 'and standard leaf' do before do delete '/leafs', 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 do |leaf| @@ -63,8 +62,8 @@ end end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'deletes the specified leafs' do diff --git a/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb b/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb index 5a46c528..4705e424 100644 --- a/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_destroy_spec.rb @@ -47,14 +47,13 @@ context 'and standard leaf' do before do delete "/leafs/#{@leafs[1].id.to_s}", headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} - @body = JSON.parse(response.body) @project.reload @group.reload @leafs.each { |leaf| leaf.reload unless leaf.id == @leafs[1].id } end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'remove the leaf' do diff --git a/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb b/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb index 52629c1c..6a455318 100644 --- a/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_update_multiple_spec.rb @@ -61,14 +61,13 @@ context 'and standard leaf' do before do put '/leafs', 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 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'updates the leaf' do diff --git a/viscoll-api/spec/requests/leafs/leafs_update_spec.rb b/viscoll-api/spec/requests/leafs/leafs_update_spec.rb index 312291c3..41a7f789 100644 --- a/viscoll-api/spec/requests/leafs/leafs_update_spec.rb +++ b/viscoll-api/spec/requests/leafs/leafs_update_spec.rb @@ -33,14 +33,13 @@ 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'} - @body = JSON.parse(response.body) @project.reload @group.reload @leafs.each { |leaf| leaf.reload } end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'edit and reconjoin the leaf' do diff --git a/viscoll-api/spec/requests/notes/notes_create_spec.rb b/viscoll-api/spec/requests/notes/notes_create_spec.rb index 953b438d..3c23e78e 100644 --- a/viscoll-api/spec/requests/notes/notes_create_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_create_spec.rb @@ -24,11 +24,10 @@ context 'and standard notes' do before do post '/notes', 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) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'adds a note to the project' do diff --git a/viscoll-api/spec/requests/notes/notes_create_type_spec.rb b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb index e0a5f4b0..d0f2ce3c 100644 --- a/viscoll-api/spec/requests/notes/notes_create_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_create_type_spec.rb @@ -26,7 +26,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should add the type to the project' do diff --git a/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb index 1936b844..fb8a3481 100644 --- a/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_delete_type_spec.rb @@ -37,7 +37,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should remove the type from the project' do diff --git a/viscoll-api/spec/requests/notes/notes_destroy_spec.rb b/viscoll-api/spec/requests/notes/notes_destroy_spec.rb index 564bf7f1..1fd6c064 100644 --- a/viscoll-api/spec/requests/notes/notes_destroy_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_destroy_spec.rb @@ -26,8 +26,8 @@ delete '/notes/'+@note.id, 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) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'deletes the note' do diff --git a/viscoll-api/spec/requests/notes/notes_link_spec.rb b/viscoll-api/spec/requests/notes/notes_link_spec.rb index acbf1de7..8d3e4a97 100644 --- a/viscoll-api/spec/requests/notes/notes_link_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_link_spec.rb @@ -46,7 +46,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should add the note to the target' do @@ -71,7 +71,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should add the note to the target' do @@ -88,7 +88,7 @@ objects: [ { id: @side.id.to_str, - type: "Side" + type: "Recto" } ] } @@ -97,7 +97,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should add the note to the target' do @@ -166,7 +166,7 @@ objects: [ { id: @side2.id.to_str, - type: "Side" + type: "Recto" } ] } diff --git a/viscoll-api/spec/requests/notes/notes_unlink_spec.rb b/viscoll-api/spec/requests/notes/notes_unlink_spec.rb index f53402a7..d8d8c8cd 100644 --- a/viscoll-api/spec/requests/notes/notes_unlink_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_unlink_spec.rb @@ -46,7 +46,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should remove the note from the target' do @@ -70,7 +70,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should remove the note from the target' do @@ -97,7 +97,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should remove the note from the target' do diff --git a/viscoll-api/spec/requests/notes/notes_update_spec.rb b/viscoll-api/spec/requests/notes/notes_update_spec.rb index 4e3b90aa..722ea894 100644 --- a/viscoll-api/spec/requests/notes/notes_update_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_update_spec.rb @@ -35,8 +35,8 @@ @note.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'Updates the note' do diff --git a/viscoll-api/spec/requests/notes/notes_update_type_spec.rb b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb index 8268e5e9..71fdf4d9 100644 --- a/viscoll-api/spec/requests/notes/notes_update_type_spec.rb +++ b/viscoll-api/spec/requests/notes/notes_update_type_spec.rb @@ -41,7 +41,7 @@ end it 'should return 200' do - expect(response).to have_http_status(:ok) + expect(response).to have_http_status(:no_content) end it 'should remove the type from the project' do diff --git a/viscoll-api/spec/requests/projects/create_projects_spec.rb b/viscoll-api/spec/requests/projects/create_projects_spec.rb index da51939e..9fecbc91 100644 --- a/viscoll-api/spec/requests/projects/create_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/create_projects_spec.rb @@ -47,7 +47,7 @@ end it 'returns a new project' do - expect(Project.find(id: @body[0]['id'])).not_to be nil + expect(Project.find(id: @body["projects"][0]['id'])).not_to be nil end end @@ -63,7 +63,7 @@ end it 'returns a new project' do - expect(Project.find(id: @body[0]['id'])).not_to be nil + expect(Project.find(id: @body["projects"][0]['id'])).not_to be nil 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 index 40107e20..65780eff 100644 --- a/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/delete_manifest_projects_spec.rb @@ -38,11 +38,10 @@ 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 - @body = JSON.parse(response.body) end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'removes the manifest' do diff --git a/viscoll-api/spec/requests/projects/destroy_projects_spec.rb b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb index cc8e5eca..5f47a313 100644 --- a/viscoll-api/spec/requests/projects/destroy_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/destroy_projects_spec.rb @@ -13,12 +13,15 @@ @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: '', headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} @body = JSON.parse(response.body) end @@ -27,8 +30,8 @@ end it 'returns the remaining project' do - expect(@body.length).to equal 1 - expect(@body[0]['id']).to eq @project2.id.to_str + 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 @@ -40,7 +43,7 @@ context 'and inexistent project' do before do - delete '/projects/NONEXISTENT', params: '', headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + delete '/projects/NONEXISTENT', params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} @body = JSON.parse(response.body) end @@ -57,7 +60,7 @@ context "and somebody else's project" do before do - delete '/projects/'+@project3.id, params: '', headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + delete '/projects/'+@project3.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} end it 'returns 401' do @@ -74,7 +77,7 @@ context 'and a failed delete' do before do allow_any_instance_of(Project).to receive(:destroy).and_raise("Exception") - delete '/projects/'+@project1.id, params: '', headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => @authToken, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} @body = JSON.parse(response.body) end @@ -90,7 +93,7 @@ context 'with corrupted authorization' do before do - delete '/projects/'+@project1.id, params: '', headers: {'Authorization' => @authToken+'asdf', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'} + 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 @@ -105,7 +108,7 @@ context 'with empty authorization' do before do - delete '/projects/'+@project1.id, params: '', headers: {'Authorization' => ""} + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => ""} end it 'returns an bad request error' do @@ -119,7 +122,7 @@ context 'invalid authorization' do before do - delete '/projects/'+@project1.id, params: '', headers: {'Authorization' => "123456789"} + delete '/projects/'+@project1.id, params: @deleteParameters.to_json, headers: {'Authorization' => "123456789"} end it 'returns an bad request error' do 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..8128f8d3 --- /dev/null +++ b/viscoll-api/spec/requests/projects/export_projects_spec.rb @@ -0,0 +1,271 @@ +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') + @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') + @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 + + 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", 'nestLevel'=>1}, 'tacketed'=>[], 'sewing'=>[], 'parentOrder'=>nil, 'memberOrders'=>["Leaf_1", "Leaf_2", "Group_2", "Leaf_5", "Leaf_6"]}, + '2' => {'params'=>{'type'=>"Quire", 'title'=>"Group 2", 'nestLevel'=>2}, 'tacketed'=>[], 'sewing'=>[], 'parentOrder'=>1, 'memberOrders'=>["Leaf_3", "Leaf_4"]} + }) + expect(export_result['Leafs']).to eq({ + '1' => {'params'=>{'material'=>"Paper", 'type'=>"Original", 'attachment_method'=>"None", '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", 'attachment_method'=>"None", '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", 'attachment_method'=>"None", '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", 'attachment_method'=>"None", '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", 'attachment_method'=>"None", '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", 'attachment_method'=>"None", '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'=>"1R", 'texture'=>"Hair", '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'=>"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} + }) + expect(export_result['Versos']).to eq({ + '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} + }) + 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/#{@testimage.id}_#{@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/#{@testimage.id}_#{@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("taxonomy[xml|id='side_texture'] term").collect { |t| [t['xml:id'], t.text] }).to include( + ['side_texture_hair', 'Hair'], + ['side_texture_flesh', 'Flesh'] + ) + 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_texture_hair https://dummy.library.utoronto.ca/images/'+@testimage.id.to_s+'_pixel.png #manifest_DIYImages'], + ['#ravenna_384_2339-1-2', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-2-3', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-2-4', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-3', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-4', 'recto', '#side_texture_hair'], + ['#ravenna_384_2339-1-1', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2-3', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-2-4', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-3', 'verso', '#side_texture_flesh'], + ['#ravenna_384_2339-1-4', 'verso', '#side_texture_flesh'] + ) + 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 index ed9285d4..0e7410fe 100644 --- a/viscoll-api/spec/requests/projects/index_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/index_projects_spec.rb @@ -24,9 +24,9 @@ end it "contains the user's own projects only" do - expect(@body.length).to eq 2 - expect(@body[0]['id']).to eq @project2.id.to_str - expect(@body[1]['id']).to eq @project1.id.to_str + 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 diff --git a/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb index 8d4ff4fe..ab2fed76 100644 --- a/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/update_manifest_projects_spec.rb @@ -40,11 +40,10 @@ 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 - @body = JSON.parse(response.body) end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'rename the manifest' do diff --git a/viscoll-api/spec/requests/projects/update_projects_spec.rb b/viscoll-api/spec/requests/projects/update_projects_spec.rb index e8acddce..bc864c49 100644 --- a/viscoll-api/spec/requests/projects/update_projects_spec.rb +++ b/viscoll-api/spec/requests/projects/update_projects_spec.rb @@ -47,7 +47,7 @@ end it 'returns the changed project' do - expect(@body[0]['id']).to eq @project1.id.to_str + expect(@body["projects"][0]['id']).to eq @project1.id.to_str end it 'changes the right project' do diff --git a/viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb b/viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb index 023ac4e8..fb65f6e9 100644 --- a/viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb +++ b/viscoll-api/spec/requests/sides/sides_updateMultiplce_spec.rb @@ -44,8 +44,8 @@ @side2.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'Updates the sides' do diff --git a/viscoll-api/spec/requests/sides/sides_update_spec.rb b/viscoll-api/spec/requests/sides/sides_update_spec.rb index bd0ee03f..2942a8bd 100644 --- a/viscoll-api/spec/requests/sides/sides_update_spec.rb +++ b/viscoll-api/spec/requests/sides/sides_update_spec.rb @@ -37,8 +37,8 @@ @side1.reload end - it 'returns 200' do - expect(response).to have_http_status(:ok) + it 'returns 204' do + expect(response).to have_http_status(:no_content) end it 'Updates the side' do diff --git a/viscoll-api/spec/spec_helper.rb b/viscoll-api/spec/spec_helper.rb index 4f662aaf..f5a6e864 100644 --- a/viscoll-api/spec/spec_helper.rb +++ b/viscoll-api/spec/spec_helper.rb @@ -60,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 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..66747bde --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/groupActions.spec.js @@ -0,0 +1,313 @@ +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', + attachment_method: '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', + attachment_method: '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..a4dd2502 --- /dev/null +++ b/viscoll-app/__test__/actions/frontendBeforeActions/leafActions.spec.js @@ -0,0 +1,346 @@ +import { + createLeaves, + updateLeaf, + updateLeaves, + deleteLeaf, + deleteLeaves, + autoConjoinLeafs, + generateFolioNumbers, +} 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', + attachment_method: '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 generateFolioNumbers', () => { + 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); + + const gotState = generateFolioNumbers(leafPayload, beforeState); + + 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"; + + 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__/actions/projectActions.spec.js b/viscoll-app/__test__/actions/projectActions.spec.js deleted file mode 100644 index 9875e7b7..00000000 --- a/viscoll-app/__test__/actions/projectActions.spec.js +++ /dev/null @@ -1,94 +0,0 @@ -import { - loadProject, - createProject, - updateProject, - deleteProject, - loadProjects -} from '../../src/actions/projectActions'; - - -describe('>>>A C T I O N --- Test projectActions', () => { - - // it('+++ actionCreator loadProject', () => { - // const projectID = "123456"; - // const loadProjectAction = loadProject(projectID); - // expect(loadProjectAction).toEqual({ - // types: ['SHOW_LOADING','LOAD_PROJECT_SUCCESS','LOAD_PROJECT_FAILED'], - // payload: { - // request : { - // url: `/projects/${projectID}`, - // method: 'get', - // successMessage: "" , - // errorMessage: "Ooops! Something went wrong", - // }, - // } - // }) - // }); - - it('+++ actionCreator createProject', () => { - const project = "new project object"; - const createProjectAction = createProject(project); - expect(createProjectAction).toEqual({ - types: ['SHOW_LOADING','CREATE_PROJECT_SUCCESS','CREATE_PROJECT_FAILED'], - payload: { - request : { - url: `/projects`, - method: 'post', - data: project, - successMessage: "Successfully created the project" , - errorMessage: "Ooops! Something went wrong" - } - } - }) - }); - - it('+++ actionCreator updateProject', () => { - const project = "new project object"; - const projectID = "123456"; - const updateProjectAction = updateProject(projectID, project); - expect(updateProjectAction).toEqual({ - types: ['NO_LOADING','UPDATE_PROJECT_SUCCESS','UPDATE_PROJECT_FAILED'], - payload: { - request : { - url: `/projects/${projectID}`, - method: 'put', - data: {project}, - successMessage: "Successfully updated the project" , - errorMessage: "Ooops! Something went wrong" - } - } - }) - }); - - it('+++ actionCreator deleteProject', () => { - const projectID = "123456"; - const deleteProjectAction = deleteProject(projectID); - expect(deleteProjectAction).toEqual({ - types: ['SHOW_LOADING','DELETE_PROJECT_SUCCESS','DELETE_PROJECT_FAILED'], - payload: { - request : { - url: `/projects/${projectID}`, - method: 'delete', - successMessage: "Successfully deleted the project" , - errorMessage: "Ooops! Something went wrong" - } - } - }) - }); - - it('+++ actionCreator loadProjects', () => { - const loadProjectsAction = loadProjects(); - expect(loadProjectsAction).toEqual({ - types: ['NO_LOADING','LOAD_PROJECTS_SUCCESS','LOAD_PROJECTS_FAILED'], - payload: { - request : { - url: `/projects`, - method: 'get', - successMessage: "" , - errorMessage: "Ooops! Something went wrong" - } - } - }) - }); - -}); diff --git a/viscoll-app/__test__/actions/userActions.spec.js b/viscoll-app/__test__/actions/userActions.spec.js deleted file mode 100644 index 7beb4e02..00000000 --- a/viscoll-app/__test__/actions/userActions.spec.js +++ /dev/null @@ -1,163 +0,0 @@ -import { - login, - register, - confirm, - logout, - resetPasswordRequest, - resetPassword, - updateProfile, - deleteProfile -} from '../../src/actions/userActions'; - - -describe('>>>A C T I O N --- Test userActions', () => { - it('+++ actionCreator login', () => { - const session = { - session: { - email: "user@mail.com", - password: "secret" - } - }; - const loginAction = login(session); - expect(loginAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator register', () => { - const user = { - email: "user@mail.com", - password: "secret", - name: "user" - }; - const registerAction = register(user); - expect(registerAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator confirm', () => { - const confirmation_token = { - confirmation_token: "5951303fc9bf3c7b9a573a3f" - }; - const confirmAction = confirm(confirmation_token); - expect(confirmAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator logout', () => { - const logoutAction = logout(); - expect(logoutAction).toEqual({ - types: ['NO_LOADING','LOGOUT_SUCCESS','LOGOUT_FAILED'], - payload: { - request : { - url: `/session`, - method: 'delete', - successMessage: "You have successfully logged out" , - errorMessage: "Ooops! Something went wrong" - } - } - }) - }); - - it('+++ actionCreator resetPasswordRequest', () => { - const email = "user@mail.com"; - const resetPasswordRequestAction = resetPasswordRequest(email) - expect(resetPasswordRequestAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator resetPassword', () => { - const password = { - psasword: "secret", - password_confirmation: "secret" - }; - const reset_password_token = "5951303fc9bf3c7b9a573a3f"; - const resetPasswordAction = resetPassword(reset_password_token, password); - expect(resetPasswordAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator updateProfile', () => { - const user = {user: {id: "123", name: "batman"}}; - const userID = "123456"; - const updateProfileAction = updateProfile(user, userID); - expect(updateProfileAction).toEqual({ - 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" - } - } - }) - }); - - it('+++ actionCreator deleteProfile', () => { - const userID = "123456"; - const deleteProfileAction = deleteProfile(userID); - expect(deleteProfileAction).toEqual({ - 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" - } - } - }) - }); - -}); diff --git a/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js b/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js deleted file mode 100644 index bddbb774..00000000 --- a/viscoll-app/__test__/helpers/MultiSelectAutoComplete.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import {shallow} from 'enzyme'; -import MultiSelectAutoComplete from '../../src/helpers/MultiSelectAutoComplete'; - - -describe('>>>MULTISELECT_AUTOCOMPLETE --- Shallow Render REACT COMPONENTS',()=>{ - let wrapper; - - beforeEach(()=>{ - wrapper = shallow({}} selectedItems={[]}/>) - }) - - it('+++ render the DUMB component', () => { - expect(wrapper.length).toEqual(1) - }); - - -}); diff --git a/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js b/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js deleted file mode 100644 index b85c7a56..00000000 --- a/viscoll-app/__test__/reducers/editCollationReducer/structureRelatedReducers.spec.js +++ /dev/null @@ -1,328 +0,0 @@ -import editCollationReducer from '../../../src/reducers/editCollationReducer'; -import { initialState } from '../../../src/reducers/initialStates/active'; - - -describe('>>>R E D U C E R --- Test Collation Structure Related Actions',()=>{ - it('+++ reducer for ADD_LEAF(S)_SUCCESS', () => { - let action = { - type: "ADD_LEAF(S)_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for ADD_GROUP(S)_SUCCESS', () => { - let action = { - type: "ADD_GROUP(S)_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_GROUP_SUCCESS', () => { - let action = { - type: "UPDATE_GROUP_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_GROUPS_SUCCESS', () => { - let action = { - type: "UPDATE_GROUPS_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_LEAF_SUCCESS', () => { - let action = { - type: "UPDATE_LEAF_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_LEAFS_SUCCESS', () => { - let action = { - type: "UPDATE_LEAFS_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_SIDE_SUCCESS', () => { - let action = { - type: "UPDATE_SIDE_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for UPDATE_SIDES_SUCCESS', () => { - let action = { - type: "UPDATE_SIDES_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for DELETE_LEAF_SUCCESS', () => { - let action = { - type: "DELETE_LEAF_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for DELETE_LEAFS_SUCCESS', () => { - let action = { - type: "DELETE_LEAFS_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for DELETE_GROUP_SUCCESS', () => { - let action = { - type: "DELETE_GROUP_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for DELETE_GROUPS_SUCCESS', () => { - let action = { - type: "DELETE_GROUPS_SUCCESS", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - - // Failing Actions - it('+++ reducer for UPDATE_GROUP_FAILED', () => { - let action = { - type: "UPDATE_GROUP_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for UPDATE_GROUPS_FAILED', () => { - let action = { - type: "UPDATE_GROUPS_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for UPDATE_SIDE_FAILED', () => { - let action = { - type: "UPDATE_SIDE_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for UPDATE_SIDES_FAILED', () => { - let action = { - type: "UPDATE_SIDES_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for UPDATE_LEAF_FAILED', () => { - let action = { - type: "UPDATE_LEAF_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for UPDATE_LEAFS_FAILED', () => { - let action = { - type: "UPDATE_LEAFS_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for ADD_LEAF(S)_FAILED', () => { - let action = { - type: "ADD_LEAF_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for ADD_GROUP(S)_FAILED', () => { - let action = { - type: "ADD_GROUP_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for DELETE_LEAF_FAILED', () => { - let action = { - type: "DELETE_LEAF_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for DELETE_LEAFS_FAILED', () => { - let action = { - type: "DELETE_LEAFS_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for DELETE_GROUP_FAILED', () => { - let action = { - type: "DELETE_GROUP_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for DELETE_GROUPS_FAILED', () => { - let action = { - type: "DELETE_GROUPS_FAILED", - payload: "the full project structure this leaf belongs to" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); - it('+++ reducer for LOAD_PROJECT_SUCCESS', () => { - let action = { - type: "LOAD_PROJECT_SUCCESS", - payload: "project object" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...state, - project: action.payload, - notes: action.payload.notes, - noteTypes: action.payload.noteTypes - }) - }); - it('+++ reducer for LOAD_PROJECT_FAILED', () => { - let action = { - type: "LOAD_PROJECT_FAILED", - payload: "project object" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState - }) - }); - it('+++ reducer for persist/REHYDRATE', () => { - let action = { - type: "persist/REHYDRATE", - payload: {active: "saved state"} - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - ...action.payload.active - }) - }); - it('+++ reducer for axios error config', () => { - let action = { - type: "LOAD_PROJECT_FAILED", - error: "some error" - }; - let state = editCollationReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - }) - }); -}); diff --git a/viscoll-app/__test__/reducers/userReducer.spec.js b/viscoll-app/__test__/reducers/userReducer.spec.js deleted file mode 100644 index afb551a0..00000000 --- a/viscoll-app/__test__/reducers/userReducer.spec.js +++ /dev/null @@ -1,220 +0,0 @@ -import userReducer from '../../src/reducers/userReducer'; -import { initialState } from '../../src/reducers/initialStates/user'; - - -const authenticatedState = { - authenticated: true, - token: "secretToken", - id: "userID", - name: "user", - email: "user@mail.com", - errors: { - login: {errorMessage: ""}, - register: {email: "", password: ""}, - update: {password: "", current_password: "", email: ""}, - confirmation: "", - } -} - - -describe('>>>R E D U C E R --- Test userReducer',()=>{ - it('+++ reducer for LOGIN_SUCCESS', () => { - let state = initialState - state = userReducer( - state, - { - type: "LOGIN_SUCCESS", - payload: { - session: { - jwt: "secretToken", - id: "userID", - name: "user", - email: "user@mail.com", - lastLoggedIn: "2017-07-12T16:44:31.756Z" - } - } - } - ) - expect(state).toEqual({ - ...state, - authenticated: true, - token: "secretToken", - id: "userID", - name: "user", - email: "user@mail.com", - lastLoggedIn: "2017-07-12T16:44:31.756Z" - }) - }); - - it('+++ reducer for LOGIN_FAILED', () => { - let state = initialState - state = userReducer( - state, - { - type: "LOGIN_FAILED", - payload: { - errors: { - session: ["invalid email / password"] - } - } - } - ) - expect(state).toEqual({ - ...state, - errors: { - ...state.errors, - login: { - errorMessage: ["invalid email / password"] - }, - } - }) - }); - - it('+++ reducer for REGISTER_SUCCESS', () => { - let state = initialState - state = userReducer( - state, - { - type: "REGISTER_SUCCESS" - } - ) - expect(state).toEqual({ - ...state, - registerSuccess: true - }) - }); - - it('+++ reducer for REGISTER_FAILED', () => { - let state = initialState - state = userReducer( - state, - { - type: "REGISTER_FAILED", - payload: { - errors: { - email: ["is already taken"], - password: ["can't be blank"] - } - } - } - ) - expect(state).toEqual({ - ...state, - errors: { - ...state.errors, - register: { - email: ["is already taken"], - password: ["can't be blank"] - } - } - }) - }); - - it('+++ reducer for CONFIRM_SUCCESS', () => { - let state = authenticatedState - state = userReducer(state, {type: "CONFIRM_SUCCESS"}) - expect(state).toEqual({...authenticatedState}) - }); - - it('+++ reducer for CONFIRM_FAILED', () => { - let state = authenticatedState - state = userReducer(state, { - type: "CONFIRM_FAILED", - payload: { errors: { confirmation_token: ["is expired"] } }, - }) - expect(state).toEqual({ - ...authenticatedState, - errors: { - ...authenticatedState.errors, - confirmation: "Confirmation token is expired" - } - }) - }); - - it('+++ reducer for RESET_SUCCESS', () => { - let state = authenticatedState - state = userReducer(state, {type: "RESET_SUCCESS"}) - expect(state).toEqual({...authenticatedState}) - }); - - it('+++ reducer for RESET_FAILED', () => { - let state = authenticatedState - state = userReducer(state, {type: "RESET_FAILED"}) - expect(state).toEqual({...authenticatedState}) - }); - - it('+++ reducer for LOGOUT_SUCCESS', () => { - let state = authenticatedState - state = userReducer(state, {type: "LOGOUT_SUCCESS"}) - expect(state).toEqual({...initialState}) - }); - - it('+++ reducer for LOGOUT_FAILED', () => { - let state = authenticatedState - state = userReducer(state, {type: "LOGOUT_FAILED"}) - expect(state).toEqual({...authenticatedState}) - }); - - it('+++ reducer for UPDATE_PROFILE_SUCCESS', () => { - let state = authenticatedState - let action = { - type: "UPDATE_PROFILE_SUCCESS", - payload: {user: {id: 1, name: "batman"}, errors: "errors"} - }; - state = userReducer(state, action) - expect(state).toEqual({ - ...authenticatedState, - errors: initialState.errors, - ...action.payload - }) - }); - - it('+++ reducer for UPDATE_PROFILE_FAILED', () => { - let state = authenticatedState - let action = { - type: "UPDATE_PROFILE_FAILED", - payload: {user: {id: 1, name: "batman"}, errors: "errors"} - }; - state = userReducer(state, action) - expect(state).toEqual({ - ...authenticatedState, - errors: { - ...state.errors, - update: {...state.errors.update, ...action.payload} - } - }) - }); - - it('+++ reducer for persist/REHYDRATE', () => { - let action = { - type: "persist/REHYDRATE", - payload: {user: {id: 1, name: "batman"}, errors: "errors"} - }; - let state = userReducer(initialState, action); - expect(state).toEqual({ - ...initialState, - ...action.payload.user, - errors: initialState.errors - }); - }); - - it('+++ reducer for axios error config', () => { - let action = { - type: "RESET_FAILED", - error: "some error" - }; - let state = userReducer(initialState, action); - expect(state).toEqual(initialState) - }); - - it('+++ reducer for DEFAULT CASE', () => { - let action = { - type: "SOMETHING ELSE" - }; - let state = userReducer(initialState, action); - expect(state).toEqual({ - ...initialState - }) - }); - -}); 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/projectState001.js b/viscoll-app/__test__/testData/projectState001.js new file mode 100644 index 00000000..85500509 --- /dev/null +++ b/viscoll-app/__test__/testData/projectState001.js @@ -0,0 +1,4753 @@ +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', + attachment_method: 'None', + 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', + attachment_method: 'None', + 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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: 'None', + 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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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..2f0d31eb --- /dev/null +++ b/viscoll-app/__test__/testData/state001.js @@ -0,0 +1,4753 @@ +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', + attachment_method: 'None', + 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', + attachment_method: 'None', + 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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: 'None', + 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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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', + attachment_method: '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/docs/assets/viscoll_component_tree_diagram.svg b/viscoll-app/docs/assets/viscoll_component_tree_diagram.svg deleted file mode 100644 index 674b9c68..00000000 --- a/viscoll-app/docs/assets/viscoll_component_tree_diagram.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg b/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg deleted file mode 100644 index e3b37af8..00000000 --- a/viscoll-app/docs/assets/viscoll_data_flow_diagram.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/viscoll-app/docs/introduction.md b/viscoll-app/docs/introduction.md deleted file mode 100644 index 51c27107..00000000 --- a/viscoll-app/docs/introduction.md +++ /dev/null @@ -1,5 +0,0 @@ -## Data flow of Viscoll -![Data flow diagram of Viscoll](viscoll_data_flow_diagram.svg) - -## Component tree -![Component tree diagram of Viscoll](viscoll_component_tree_diagram.svg) \ No newline at end of file diff --git a/viscoll-app/package-lock.json b/viscoll-app/package-lock.json index 9e8efa90..515d15b4 100644 --- a/viscoll-app/package-lock.json +++ b/viscoll-app/package-lock.json @@ -2,6 +2,7 @@ "name": "viscoll-app", "version": "0.1.0", "lockfileVersion": 1, + "requires": true, "dependencies": { "abab": { "version": "1.0.3", @@ -12,7 +13,11 @@ "accepts": { "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true + "dev": true, + "requires": { + "mime-types": "2.1.16", + "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" + } }, "acorn": { "version": "5.1.1", @@ -22,6 +27,9 @@ "acorn-dynamic-import": { "version": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + }, "dependencies": { "acorn": { "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -34,6 +42,9 @@ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "dev": true, + "requires": { + "acorn": "4.0.13" + }, "dependencies": { "acorn": { "version": "4.0.13", @@ -47,6 +58,9 @@ "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz" + }, "dependencies": { "acorn": { "version": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", @@ -55,20 +69,6 @@ } } }, - "acorn-object-spread": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz", - "integrity": "sha1-SOrQ9KjrFplaF6Dbn/xqyq2kumg=", - "dev": true, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, "address": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/address/-/address-1.0.2.tgz", @@ -78,7 +78,11 @@ "ajv": { "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true + "dev": true, + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + } }, "ajv-keywords": { "version": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", @@ -87,7 +91,12 @@ }, "align-text": { "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=" + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } }, "alphanum-sort": { "version": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", @@ -108,7 +117,10 @@ "ansi-align": { "version": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=", - "dev": true + "dev": true, + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + } }, "ansi-escapes": { "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", @@ -132,28 +144,44 @@ "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==" + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "requires": { + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + } }, "append-transform": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true + "dev": true, + "requires": { + "default-require-extensions": "1.0.0" + } }, "argparse": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } }, "aria-query": { "version": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz", "integrity": "sha1-heMVLNjMW6sY2+1hzZxPzlT6ecM=", - "dev": true + "dev": true, + "requires": { + "ast-types-flow": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + } }, "arr-diff": { "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=" + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "1.1.0" + } }, "arr-flatten": { "version": "1.1.0", @@ -186,13 +214,11 @@ "array-includes": { "version": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true - }, - "array-iterate": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.1.tgz", - "integrity": "sha1-hlv3+K851rCYLGCQKRSsdrwBCPY=", - "dev": true + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "es-abstract": "1.8.0" + } }, "array-map": { "version": "0.0.0", @@ -209,7 +235,10 @@ "array-union": { "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true + "dev": true, + "requires": { + "array-uniq": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" + } }, "array-uniq": { "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", @@ -239,11 +268,19 @@ "asn1.js": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", - "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=" + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "requires": { + "bn.js": "4.11.8", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } }, "assert": { "version": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=" + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "https://registry.npmjs.org/util/-/util-0.10.3.tgz" + } }, "assert-plus": { "version": "0.2.0", @@ -264,7 +301,10 @@ "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==" + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "requires": { + "lodash": "4.17.4" + } }, "async-each": { "version": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -279,7 +319,15 @@ "autoprefixer": { "version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.0.tgz", "integrity": "sha1-rkkTrcIh+mylrTpvgDn2pcBrOHc=", - "dev": true + "dev": true, + "requires": { + "browserslist": "2.3.0", + "caniuse-lite": "1.0.30000712", + "normalize-range": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "num2fraction": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "postcss": "6.0.8", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + } }, "aws-sign2": { "version": "0.6.0", @@ -296,182 +344,352 @@ "axios": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz", - "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=" + "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=", + "requires": { + "follow-redirects": "1.2.4", + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } }, "axobject-query": { "version": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", - "dev": true + "dev": true, + "requires": { + "ast-types-flow": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + } }, "babel-code-frame": { "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "3.0.2" + } }, "babel-core": { "version": "6.25.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=", - "dev": true + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "babel-helpers": "6.24.1", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-register": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "lodash": "4.17.4", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "babel-eslint": { "version": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", - "dev": true + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz" + } }, "babel-generator": { "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", - "dev": true + "dev": true, + "requires": { + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "detect-indent": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "lodash": "4.17.4", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "trim-right": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "babel-helper-builder-react-jsx": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.24.1.tgz", "integrity": "sha1-CteRfjPI11HmRtrKTnfMGTd9LLw=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.25.0", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "babel-helper-define-map": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz", "integrity": "sha1-epdH8ljYlH0y1RX2qhx70CIEoIA=", - "dev": true + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "6.24.1", + "babel-runtime": "6.25.0", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "babel-helper-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz", "integrity": "sha1-024i+rEAjXnYhkjjIRaGgShFbOg=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "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 + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "babel-jest": { "version": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=", - "dev": true + "dev": true, + "requires": { + "babel-core": "6.25.0", + "babel-plugin-istanbul": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", + "babel-preset-jest": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz" + } }, "babel-loader": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.1.tgz", "integrity": "sha1-uHE0yLEuPkwqlOBUYIW8aAorhIg=", "dev": true, + "requires": { + "find-cache-dir": "1.0.0", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + }, "dependencies": { "find-cache-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true + "dev": true, + "requires": { + "commondir": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "make-dir": "1.0.0", + "pkg-dir": "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 + "dev": true, + "requires": { + "locate-path": "2.0.0" + } }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true + "dev": true, + "requires": { + "find-up": "2.1.0" + } } } }, "babel-messages": { "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0" + } }, "babel-plugin-dynamic-import-node": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.0.2.tgz", "integrity": "sha1-rbW8j0iokxFUA5WunwzD7UsQuy4=", - "dev": true + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "babel-plugin-istanbul": { "version": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", "integrity": "sha1-GN3oS/POMp/d8/QQP66SFFbY5Yc=", "dev": true, + "requires": { + "find-up": "2.1.0", + "istanbul-lib-instrument": "1.7.4", + "test-exclude": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz" + }, "dependencies": { "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true + "dev": true, + "requires": { + "locate-path": "2.0.0" + } } } }, @@ -543,127 +761,240 @@ "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 + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-generators": "6.13.0", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-explode-class": "6.24.1", + "babel-plugin-syntax-decorators": "6.13.0", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0" + } }, "babel-plugin-transform-es2015-block-scoping": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz", "integrity": "sha1-dsKV3DpHQbFmWt/TFnIV3P8ypXY=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "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 + "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": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "babel-plugin-transform-es2015-modules-commonjs": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", "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" + }, "dependencies": { "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 + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "3.0.2" + } }, "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=", - "dev": true + "dev": true, + "requires": { + "core-js": "2.5.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 + "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 + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "4.17.4" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "4.17.4", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } }, "babylon": { "version": "6.18.0", @@ -677,136 +1008,232 @@ "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 + "dev": true, + "requires": { + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-regex": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-regex": "6.24.1", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "6.18.0", + "babel-runtime": "6.25.0" + } }, "babel-plugin-transform-object-rest-spread": { "version": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", "integrity": "sha1-h11ryb52HFiirj/u5dxIldjH+SE=", - "dev": true + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "babel-runtime": "6.25.0" + } }, "babel-plugin-transform-react-constant-elements": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-constant-elements/-/babel-plugin-transform-react-constant-elements-6.23.0.tgz", "integrity": "sha1-LxGb9NLN1F65uqrldAU8YE9hR90=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-helper-builder-react-jsx": "6.24.1", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.0" + } }, "babel-plugin-transform-regenerator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", "integrity": "sha1-uNowWtQ8PJm0hI5P5AN7dw0jxBg=", - "dev": true + "dev": true, + "requires": { + "regenerator-transform": "0.9.11" + } }, "babel-plugin-transform-runtime": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.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 + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } }, "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": { "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.5.0", + "regenerator-runtime": "0.11.0" + }, "dependencies": { "regenerator-runtime": { "version": "0.11.0", @@ -826,59 +1253,171 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.5.2.tgz", "integrity": "sha1-zUrpCm6Utwn5c3SzPl+LmDVWre8=", - "dev": true + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.24.1", + "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.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.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.26.0", + "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.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-regenerator": "6.24.1", + "browserslist": "2.3.0", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "semver": "5.4.1" + } }, "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 + "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.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.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.26.0", + "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.23.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 + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "6.22.0" + } }, "babel-preset-jest": { "version": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=", - "dev": true + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz" + } }, "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 + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-transform-react-display-name": "6.25.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": "3.0.1", "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-3.0.1.tgz", "integrity": "sha1-i3RMvkf9V8ho5vkTVSzq4mrjGGA=", - "dev": true + "dev": true, + "requires": { + "babel-plugin-dynamic-import-node": "1.0.2", + "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-object-rest-spread": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz", + "babel-plugin-transform-react-constant-elements": "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-plugin-transform-regenerator": "6.24.1", + "babel-plugin-transform-runtime": "6.23.0", + "babel-preset-env": "1.5.2", + "babel-preset-react": "6.24.1" + } }, "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 + "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 + "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": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz" + } }, "babel-register": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", - "dev": true + "dev": true, + "requires": { + "babel-core": "6.25.0", + "babel-runtime": "6.25.0", + "core-js": "2.5.0", + "home-or-tmp": "2.0.0", + "lodash": "4.17.4", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "source-map-support": "0.4.15" + } }, "babel-runtime": { "version": "6.25.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", + "requires": { + "core-js": "2.5.0", + "regenerator-runtime": "0.10.5" + }, "dependencies": { "regenerator-runtime": { "version": "0.10.5", @@ -890,29 +1429,47 @@ "babel-template": { "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "lodash": "4.17.4" + } }, "babel-traverse": { "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", - "dev": true + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "4.17.4" + } }, "babel-types": { "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "4.17.4", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } }, "babylon": { "version": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", "integrity": "sha1-Pot0AriNIsNCPhN6FXeIOxX/hpo=", "dev": true }, - "bail": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", - "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q=", - "dev": true - }, "balanced-match": { "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" @@ -936,7 +1493,10 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, - "optional": true + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } }, "big.js": { "version": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", @@ -966,17 +1526,31 @@ "version": "2.10.1", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true + "dev": true, + "requires": { + "hoek": "2.16.3" + } }, "bowser": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.7.2.tgz", - "integrity": "sha1-uUzGklumteB8QhpY5gHORhEmRXI=" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.8.1.tgz", + "integrity": "sha512-NMPaR8ILtdLSWzxQtEs16XbxMcY8ohWGQ5V+TZSJS3fNUt/PBAGkF6YWO9B/4qWE23bK3o0moQKq8UyFEosYkA==" }, "boxen": { "version": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=", "dev": true, + "requires": { + "ansi-align": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "cli-boxes": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "filled-array": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "widest-line": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz" + }, "dependencies": { "camelcase": { "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -987,11 +1561,20 @@ }, "brace-expansion": { "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } }, "braces": { "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=" + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "preserve": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + } }, "brorand": { "version": "1.1.0", @@ -1003,6 +1586,9 @@ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", "dev": true, + "requires": { + "resolve": "1.1.7" + }, "dependencies": { "resolve": { "version": "1.1.7", @@ -1015,67 +1601,92 @@ "browserify-aes": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", - "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=" + "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.0", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } }, "browserify-cipher": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=" + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "requires": { + "browserify-aes": "1.0.6", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.0" + } }, "browserify-des": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=" + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } }, "browserify-rsa": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=" + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.5" + } }, "browserify-sign": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=" + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "elliptic": "6.4.0", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "parse-asn1": "5.1.0" + } }, "browserify-zlib": { "version": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=" + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "requires": { + "pako": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz" + } }, "browserslist": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.3.0.tgz", "integrity": "sha512-jDr9Mea+n+FwI+kR0ce7rXCFBoM7hbL80G/th7oPxuNSK4V5J3LPMHB5vykjeI2h7fgSihBbSdoJPmzUC0606Q==", - "dev": true + "dev": true, + "requires": { + "caniuse-lite": "1.0.30000712", + "electron-to-chromium": "1.3.17" + } }, "bser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true - }, - "buble": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/buble/-/buble-0.15.2.tgz", - "integrity": "sha1-VH/EdIP45egXbYKqXrzLGDsC1hM=", "dev": true, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "requires": { + "node-int64": "0.4.0" } }, "buffer": { "version": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "ieee754": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "isarray": "1.0.0" + }, "dependencies": { "isarray": { "version": "1.0.0", @@ -1106,7 +1717,10 @@ "caller-path": { "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true + "dev": true, + "requires": { + "callsites": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz" + } }, "callsites": { "version": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", @@ -1117,7 +1731,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1", + "upper-case": "1.1.3" + } }, "camelcase": { "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -1127,6 +1745,10 @@ "version": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "map-obj": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + }, "dependencies": { "camelcase": { "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -1139,11 +1761,21 @@ "version": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "dev": true, + "requires": { + "browserslist": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "caniuse-db": "1.0.30000712", + "lodash.memoize": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "lodash.uniq": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + }, "dependencies": { "browserslist": { "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true + "dev": true, + "requires": { + "caniuse-db": "1.0.30000712", + "electron-to-chromium": "1.3.17" + } } } }, @@ -1175,15 +1807,13 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "ccount": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", - "integrity": "sha1-U7ai+BW7d7nChx97mnLDol8djok=", - "dev": true - }, "center-align": { "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=" + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "lazy-cache": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" + } }, "chain-function": { "version": "1.0.0", @@ -1194,6 +1824,13 @@ "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "requires": { + "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + }, "dependencies": { "supports-color": { "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -1207,53 +1844,69 @@ "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" }, - "character-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", - "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=", - "dev": true - }, - "character-entities-html4": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.1.tgz", - "integrity": "sha1-NZoqSg9+KdPcKsmb2+Ie45Q46lA=", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", - "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8=", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz", - "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw=", - "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": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "dom-serializer": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "entities": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "htmlparser2": "3.9.2", + "lodash.assignin": "4.2.0", + "lodash.bind": "4.2.1", + "lodash.defaults": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "lodash.filter": "4.6.0", + "lodash.flatten": "4.4.0", + "lodash.foreach": "4.5.0", + "lodash.map": "4.6.0", + "lodash.merge": "4.6.0", + "lodash.pick": "4.4.0", + "lodash.reduce": "4.6.0", + "lodash.reject": "4.6.0", + "lodash.some": "4.6.0" + }, "dependencies": { "domhandler": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", - "dev": true + "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } }, "htmlparser2": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", - "dev": true + "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "domhandler": "2.4.1", + "domutils": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "entities": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "2.3.2" + } } } }, "chokidar": { "version": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=" + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "requires": { + "anymatch": "1.3.2", + "async-each": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "fsevents": "1.1.3", + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "is-binary-path": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "readdirp": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz" + } }, "ci-info": { "version": "1.0.0", @@ -1264,7 +1917,11 @@ "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==" + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "safe-buffer": "5.1.1" + } }, "circular-json": { "version": "0.3.3", @@ -1275,32 +1932,23 @@ "clap": { "version": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", "integrity": "sha1-WckP4+E3EEdG/xlGmiemNP9oyFc=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + } }, "classnames": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", - "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=", - "dev": true + "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, "clean-css": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.7.tgz", "integrity": "sha1-ua6k+FZ5iJzz6ui0A0nsTr390DI=", - "dev": true - }, - "clean-webpack-plugin": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.16.tgz", - "integrity": "sha1-QiqOFQvz1av9PRS/rLBw6A+y4j8=", "dev": true, - "dependencies": { - "rimraf": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", - "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true - } + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" } }, "cli-boxes": { @@ -1311,7 +1959,10 @@ "cli-cursor": { "version": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true + "dev": true, + "requires": { + "restore-cursor": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz" + } }, "cli-width": { "version": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", @@ -1323,15 +1974,14 @@ "resolved": "https://registry.npmjs.org/clientjs/-/clientjs-0.1.11.tgz", "integrity": "sha1-Rm4bE8Ipo8u9hIT5hTHc1lj4EFQ=" }, - "clipboard-copy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-1.2.0.tgz", - "integrity": "sha1-9qPeZaiiUvqZP8sqTgz+OqS4dp4=", - "dev": true - }, "cliui": { "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=" + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "wordwrap": "0.0.2" + } }, "clone": { "version": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", @@ -1346,33 +1996,32 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true + "dev": true, + "requires": { + "q": "1.5.0" + } }, "code-point-at": { "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, - "codemirror": { - "version": "5.28.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.28.0.tgz", - "integrity": "sha512-E/Z6050shti9v9ivl0dUClVRM4xaH204jsJmEpNYC6KDTlQwAz+5DdhLzn0tjaL/Mp1P0J1uhZokcSP2RFSwlA==", - "dev": true - }, - "collapse-white-space": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", - "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=", - "dev": true - }, "color": { "version": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true + "dev": true, + "requires": { + "clone": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "color-string": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz" + } }, "color-convert": { "version": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true + "dev": true, + "requires": { + "color-name": "1.1.3" + } }, "color-name": { "version": "1.1.3", @@ -1383,12 +2032,20 @@ "color-string": { "version": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true + "dev": true, + "requires": { + "color-name": "1.1.3" + } }, "colormin": { "version": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true + "dev": true, + "requires": { + "color": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "css-color-names": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + } }, "colors": { "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -1399,7 +2056,10 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } }, "commander": { "version": "2.11.0", @@ -1407,18 +2067,6 @@ "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true }, - "common-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-dir/-/common-dir-1.0.1.tgz", - "integrity": "sha1-T9hyCF68XyYtnMI7D/NLPkV2d/A=", - "dev": true - }, - "common-sequence": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-1.0.2.tgz", - "integrity": "sha1-MOB/P49vf5s97oVPILLTnu4Ibeg=", - "dev": true - }, "commondir": { "version": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", @@ -1428,13 +2076,25 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.11.tgz", "integrity": "sha1-FnGKdd4oPtjmBAQWJaIGRYZ5fYo=", - "dev": true + "dev": true, + "requires": { + "mime-db": "1.29.0" + } }, "compression": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.0.tgz", "integrity": "sha1-AwyfGY8WQ6BX13anOOki2kNzAS0=", - "dev": true + "dev": true, + "requires": { + "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "bytes": "2.5.0", + "compressible": "2.0.11", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "on-headers": "1.0.1", + "safe-buffer": "5.1.1", + "vary": "1.1.1" + } }, "concat-map": { "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1443,12 +2103,28 @@ "concat-stream": { "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "2.3.2", + "typedarray": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + } }, "configstore": { "version": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", "dev": true, + "requires": { + "dot-prop": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "osenv": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "write-file-atomic": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "xdg-basedir": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz" + }, "dependencies": { "uuid": { "version": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", @@ -1464,7 +2140,10 @@ }, "console-browserify": { "version": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=" + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } }, "constants-browserify": { "version": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -1513,56 +2192,9 @@ "copy-to-clipboard": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", - "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==" - }, - "copy-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.0.1.tgz", - "integrity": "sha1-lyjjg7lDFgUNDHRjlY8rhcCqggA=", - "dev": true, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - }, - "fs-extra": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", - "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", - "dev": true - }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "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-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true - } + "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==", + "requires": { + "toggle-selection": "1.0.6" } }, "core-js": { @@ -1579,6 +2211,15 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, + "requires": { + "is-directory": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "js-yaml": "3.9.1", + "minimist": "1.2.0", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "parse-json": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "require-from-string": "1.2.1" + }, "dependencies": { "minimist": { "version": "1.2.0", @@ -1591,43 +2232,88 @@ "create-ecdh": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=" + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0" + } }, "create-error-class": { "version": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true + "dev": true, + "requires": { + "capture-stack-trace": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz" + } }, "create-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=" + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "requires": { + "cipher-base": "1.0.4", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "ripemd160": "2.0.1", + "sha.js": "2.4.8" + } }, "create-hmac": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=" + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.8" + } }, "create-react-class": { "version": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", - "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=" + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=", + "requires": { + "fbjs": "0.8.14", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "which": "1.3.0" + } }, "cryptiles": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true + "dev": true, + "requires": { + "boom": "2.10.1" + } }, "crypto-browserify": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", - "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==" + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "diffie-hellman": "5.0.2", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "pbkdf2": "3.0.13", + "public-encrypt": "4.0.0", + "randombytes": "2.0.5" + } }, "css-color-names": { "version": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -1635,36 +2321,76 @@ "dev": true }, "css-in-js-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-1.0.3.tgz", - "integrity": "sha1-msfgL3Y8+F2UAXZmVl7WiltfMhU=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.0.tgz", + "integrity": "sha512-yuWmPMD9FLi50Xf3k8W8oO3WM1eVnxEGCldCLyfusQ+CgivFk0s23yst4ooW6tfxMuSa03S6uUEga9UhX6GRrA==", + "requires": { + "hyphenate-style-name": "1.0.2" + } }, "css-loader": { "version": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", "integrity": "sha1-IgMlWZ+PAEUtnOtMPKbIpmeYZC0=", "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "cssnano": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "lodash.camelcase": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-modules-extract-imports": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "postcss-modules-local-by-default": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "postcss-modules-scope": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "postcss-modules-values": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "source-list-map": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, "css-select": { "version": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true + "dev": true, + "requires": { + "boolbase": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "css-what": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "domutils": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "nth-check": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz" + } }, "css-selector-tokenizer": { "version": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", "dev": true, + "requires": { + "cssesc": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "fastparse": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "regexpu-core": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz" + }, "dependencies": { "regexpu-core": { "version": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true + "dev": true, + "requires": { + "regenerate": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "regjsgen": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "regjsparser": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz" + } } } }, @@ -1682,28 +2408,84 @@ "version": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, + "requires": { + "autoprefixer": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-calc": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "postcss-colormin": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "postcss-convert-values": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "postcss-discard-comments": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "postcss-discard-duplicates": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "postcss-discard-empty": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "postcss-discard-overridden": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "postcss-discard-unused": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "postcss-filter-plugins": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "postcss-merge-idents": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "postcss-merge-longhand": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "postcss-merge-rules": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "postcss-minify-font-values": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "postcss-minify-gradients": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "postcss-minify-params": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "postcss-minify-selectors": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "postcss-normalize-charset": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "postcss-normalize-url": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "postcss-ordered-values": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "postcss-reduce-idents": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "postcss-reduce-initial": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "postcss-reduce-transforms": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "postcss-svgo": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "postcss-unique-selectors": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "postcss-zindex": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz" + }, "dependencies": { "autoprefixer": { "version": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true + "dev": true, + "requires": { + "browserslist": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "caniuse-db": "1.0.30000712", + "normalize-range": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "num2fraction": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + } }, "browserslist": { "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true + "dev": true, + "requires": { + "caniuse-db": "1.0.30000712", + "electron-to-chromium": "1.3.17" + } }, "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, "csso": { "version": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true + "dev": true, + "requires": { + "clap": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "cssom": { "version": "0.3.2", @@ -1715,16 +2497,25 @@ "version": "0.2.37", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", - "dev": true + "dev": true, + "requires": { + "cssom": "0.3.2" + } }, "currently-unhandled": { "version": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true + "dev": true, + "requires": { + "array-find-index": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + } }, "d": { "version": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=" + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "0.10.26" + } }, "damerau-levenshtein": { "version": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", @@ -1736,6 +2527,9 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -1751,7 +2545,10 @@ }, "debug": { "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=" + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } }, "decamelize": { "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1771,12 +2568,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true + "dev": true, + "requires": { + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + } }, "define-properties": { "version": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true + "dev": true, + "requires": { + "foreach": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "object-keys": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + } }, "defined": { "version": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -1786,7 +2590,16 @@ "del": { "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true + "dev": true, + "requires": { + "globby": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "is-path-cwd": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "is-path-in-cwd": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz" + } }, "delayed-stream": { "version": "1.0.0", @@ -1803,7 +2616,11 @@ "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=" + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } }, "destroy": { "version": "1.0.4", @@ -1814,7 +2631,10 @@ "detect-indent": { "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true + "dev": true, + "requires": { + "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + } }, "detect-node": { "version": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", @@ -1825,7 +2645,11 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.3.tgz", "integrity": "sha1-pNLwYddXoDTs83xRQmCph1DysTE=", - "dev": true + "dev": true, + "requires": { + "address": "1.0.2", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + } }, "diff": { "version": "3.3.0", @@ -1836,12 +2660,21 @@ "diffie-hellman": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=" + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.0", + "randombytes": "2.0.5" + } }, "doctrine": { "version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true, + "requires": { + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "isarray": "1.0.0" + }, "dependencies": { "isarray": { "version": "1.0.0", @@ -1855,6 +2688,9 @@ "version": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "dev": true, + "requires": { + "utila": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz" + }, "dependencies": { "utila": { "version": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", @@ -1872,6 +2708,10 @@ "version": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "entities": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + }, "dependencies": { "domelementtype": { "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", @@ -1883,7 +2723,10 @@ "dom-urls": { "version": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=", - "dev": true + "dev": true, + "requires": { + "urijs": "https://registry.npmjs.org/urijs/-/urijs-1.18.10.tgz" + } }, "dom-walk": { "version": "0.1.1", @@ -1902,17 +2745,27 @@ "domhandler": { "version": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true + "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } }, "domutils": { "version": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true + "dev": true, + "requires": { + "dom-serializer": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } }, "dot-prop": { "version": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", - "dev": true + "dev": true, + "requires": { + "is-obj": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" + } }, "dotenv": { "version": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", @@ -1927,14 +2780,20 @@ "duplexer2": { "version": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true + "dev": true, + "requires": { + "readable-stream": "2.3.2" + } }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.1" + } }, "ee-first": { "version": "1.1.1", @@ -1951,7 +2810,16 @@ "elliptic": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=" + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "minimalistic-crypto-utils": "1.0.1" + } }, "emoji-regex": { "version": "6.5.1", @@ -1972,12 +2840,21 @@ "encoding": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=" + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.18" + } }, "enhanced-resolve": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=" + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "memory-fs": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "tapable": "0.2.8" + } }, "entities": { "version": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -1988,32 +2865,67 @@ "version": "2.9.1", "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-2.9.1.tgz", "integrity": "sha1-B9XOaRJBJA+4F78sSxjW5TAkDfY=", - "dev": true + "dev": true, + "requires": { + "cheerio": "0.22.0", + "function.prototype.name": "1.0.3", + "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": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "uuid": "3.1.0" + } }, "errno": { "version": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=" + "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", + "requires": { + "prr": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" + } }, "error-ex": { "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=" + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "requires": { + "is-arrayish": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + } }, "es-abstract": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.0.tgz", "integrity": "sha512-Cf9/h5MrXtExM20gSS55YFrGKCyPrRBjIVBtVyy8vmlsDfe0NPKMWj65tPLgzyfPuapWxh5whpXCtW4+AW5mRg==", - "dev": true + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "is-callable": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "is-regex": "1.0.4" + } }, "es-to-primitive": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true + "dev": true, + "requires": { + "is-callable": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } }, "es3ify": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz", "integrity": "sha1-rZ+l3xrjTz8x4SEbWBiy1RB439E=", + "requires": { + "esprima-fb": "3001.1.0-dev-harmony-fb", + "jstransform": "3.0.0", + "through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, "dependencies": { "esprima-fb": { "version": "3001.1.0-dev-harmony-fb", @@ -2025,21 +2937,32 @@ "es5-ext": { "version": "0.10.26", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.26.tgz", - "integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=" + "integrity": "sha1-UbISilMbcMT2dkCTpzy+u4IYY3I=", + "requires": { + "es6-iterator": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "es6-symbol": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz" + } }, "es6-iterator": { "version": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=" + "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26", + "es6-symbol": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz" + } }, "es6-map": { "version": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=" - }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", - "dev": true + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26", + "es6-iterator": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "es6-set": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "es6-symbol": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "event-emitter": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz" + } }, "es6-promise": { "version": "4.1.0", @@ -2048,15 +2971,32 @@ }, "es6-set": { "version": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=" + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26", + "es6-iterator": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "es6-symbol": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "event-emitter": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz" + } }, "es6-symbol": { "version": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=" + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26" + } }, "es6-weak-map": { "version": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=" + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26", + "es6-iterator": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "es6-symbol": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz" + } }, "escape-html": { "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2073,6 +3013,13 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, + "requires": { + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "optionator": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "source-map": "0.2.0" + }, "dependencies": { "esprima": { "version": "2.7.3", @@ -2091,18 +3038,64 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", "dev": true, - "optional": true + "optional": true, + "requires": { + "amdefine": "1.0.1" + } } } }, "escope": { "version": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=" + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "es6-weak-map": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "esrecurse": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "estraverse": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz" + } }, "eslint": { "version": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "doctrine": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "escope": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "espree": "3.5.0", + "esquery": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "estraverse": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "file-entry-cache": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "ignore": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", + "imurmurhash": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "inquirer": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "is-my-json-valid": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "is-resolvable": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "js-yaml": "3.9.1", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "levn": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "lodash": "4.17.4", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "natural-compare": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "optionator": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "path-is-inside": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "pluralize": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "progress": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "require-uncached": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "shelljs": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "table": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "text-table": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "user-home": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz" + }, "dependencies": { "strip-bom": { "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -2120,33 +3113,69 @@ "eslint-import-resolver-node": { "version": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolve": "1.4.0" + } }, "eslint-loader": { "version": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", "integrity": "sha1-ULFY3WJy3O+5fphCVIN/gaWALOA=", - "dev": true + "dev": true, + "requires": { + "find-cache-dir": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "loader-fs-cache": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "object-hash": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.8.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz" + } }, "eslint-module-utils": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "pkg-dir": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz" + } }, "eslint-plugin-flowtype": { "version": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.33.0.tgz", "integrity": "sha1-sng4FO0t3PcplTuPZf9zyQyr7ks=", - "dev": true + "dev": true, + "requires": { + "lodash": "4.17.4" + } }, "eslint-plugin-import": { "version": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", "dev": true, + "requires": { + "builtin-modules": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "contains-path": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "doctrine": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "eslint-import-resolver-node": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", + "eslint-module-utils": "2.1.1", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "lodash.cond": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "pkg-up": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz" + }, "dependencies": { "doctrine": { "version": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true + "dev": true, + "requires": { + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "isarray": "1.0.0" + } }, "isarray": { "version": "1.0.0", @@ -2159,12 +3188,26 @@ "eslint-plugin-jsx-a11y": { "version": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz", "integrity": "sha1-SpOfduwSUBBSiCMzG/lIzFczgLY=", - "dev": true + "dev": true, + "requires": { + "aria-query": "https://registry.npmjs.org/aria-query/-/aria-query-0.5.0.tgz", + "array-includes": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "ast-types-flow": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "axobject-query": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "damerau-levenshtein": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "emoji-regex": "6.5.1", + "jsx-ast-utils": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz" + } }, "eslint-plugin-react": { "version": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.0.1.tgz", "integrity": "sha1-54EH4eVZxuKxd4a7Z8LioBCtDS8=", - "dev": true + "dev": true, + "requires": { + "doctrine": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "jsx-ast-utils": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz" + } }, "esmangle-evaluator": { "version": "1.0.1", @@ -2175,7 +3218,11 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.0.tgz", "integrity": "sha1-mDWGJb3QVYYeon4oZ+pyn69GPY0=", - "dev": true + "dev": true, + "requires": { + "acorn": "5.1.1", + "acorn-jsx": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" + } }, "esprima": { "version": "4.0.0", @@ -2186,11 +3233,18 @@ "esquery": { "version": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", - "dev": true + "dev": true, + "requires": { + "estraverse": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz" + } }, "esrecurse": { "version": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=" + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "requires": { + "estraverse": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "estraverse": { "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", @@ -2209,7 +3263,11 @@ }, "event-emitter": { "version": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=" + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "es5-ext": "0.10.26" + } }, "eventemitter3": { "version": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", @@ -2223,18 +3281,27 @@ "eventsource": { "version": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", - "dev": true + "dev": true, + "requires": { + "original": "https://registry.npmjs.org/original/-/original-1.0.0.tgz" + } }, "evp_bytestokey": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", - "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=" + "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=", + "requires": { + "create-hash": "1.1.3" + } }, "exec-sh": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", "integrity": "sha1-FPdd4/INKG75MwmbLOUKkDWc7xA=", - "dev": true + "dev": true, + "requires": { + "merge": "1.2.0" + } }, "exit-hook": { "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", @@ -2243,17 +3310,53 @@ }, "expand-brackets": { "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=" + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" + } }, "expand-range": { "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=" + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" + } }, "express": { "version": "4.15.4", "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", "dev": true, + "requires": { + "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "array-flatten": "1.1.1", + "content-disposition": "0.5.2", + "content-type": "1.0.2", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "etag": "1.8.0", + "finalhandler": "1.0.4", + "fresh": "0.5.0", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "path-to-regexp": "0.1.7", + "proxy-addr": "1.1.5", + "qs": "6.5.0", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "send": "0.15.4", + "serve-static": "1.12.4", + "setprototypeof": "1.0.3", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.0", + "vary": "1.1.1" + }, "dependencies": { "path-to-regexp": { "version": "0.1.7", @@ -2279,16 +3382,30 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", "integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=", - "dev": true + "dev": true, + "requires": { + "iconv-lite": "0.4.18", + "jschardet": "1.5.1", + "tmp": "0.0.31" + } }, "extglob": { "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=" + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } }, "extract-text-webpack-plugin": { "version": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", "integrity": "sha1-aTFbiF+Hbb+W04Gfap8cynrr8Vk=", - "dev": true + "dev": true, + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "async": "2.5.0", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "webpack-sources": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz" + } }, "extsprintf": { "version": "1.0.2", @@ -2300,6 +3417,12 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", "integrity": "sha1-wY0k71CRF0pJfzGM0ksCaiXN2rQ=", + "requires": { + "acorn": "1.2.2", + "foreach": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "isarray": "0.0.1", + "object-keys": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + }, "dependencies": { "acorn": { "version": "1.2.2", @@ -2325,18 +3448,33 @@ "faye-websocket": { "version": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true + "dev": true, + "requires": { + "websocket-driver": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz" + } }, "fb-watchman": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true + "dev": true, + "requires": { + "bser": "2.0.0" + } }, "fbjs": { "version": "0.8.14", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz", "integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.14" + }, "dependencies": { "core-js": { "version": "1.2.7", @@ -2348,17 +3486,28 @@ "figures": { "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true + "dev": true, + "requires": { + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "file-entry-cache": { "version": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true + "dev": true, + "requires": { + "flat-cache": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "file-loader": { "version": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", "integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", - "dev": true + "dev": true, + "requires": { + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz" + } }, "filename-regex": { "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -2368,7 +3517,11 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } }, "filesize": { "version": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz", @@ -2377,7 +3530,14 @@ }, "fill-range": { "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=" + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "isobject": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "randomatic": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } }, "filled-array": { "version": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", @@ -2388,42 +3548,46 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "encodeurl": "1.0.1", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "on-finished": "2.3.0", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } }, "find-cache-dir": { "version": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true + "dev": true, + "requires": { + "commondir": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "pkg-dir": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz" + } }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=" - }, - "findup": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz", - "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=", - "dev": true, - "dependencies": { - "colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", - "dev": true - }, - "commander": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", - "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=", - "dev": true - } + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" } }, "flat-cache": { "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", - "dev": true + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "write": "https://registry.npmjs.org/write/-/write-0.2.1.tgz" + } }, "flatten": { "version": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -2433,7 +3597,10 @@ "follow-redirects": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.4.tgz", - "integrity": "sha512-Suw6KewLV2hReSyEOeql+UUkBVyiBm3ok1VPrVFRZnQInWpdoZbbiG5i8aJVSjTr0yQ4Ava0Sh6/joCg1Brdqw==" + "integrity": "sha512-Suw6KewLV2hReSyEOeql+UUkBVyiBm3ok1VPrVFRZnQInWpdoZbbiG5i8aJVSjTr0yQ4Ava0Sh6/joCg1Brdqw==", + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + } }, "for-in": { "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2441,7 +3608,10 @@ }, "for-own": { "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=" + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + } }, "foreach": { "version": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", @@ -2457,7 +3627,12 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.16" + } }, "forwarded": { "version": "0.1.0", @@ -2474,7 +3649,12 @@ "fs-extra": { "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jsonfile": "3.0.1", + "universalify": "0.1.1" + } }, "fs.realpath": { "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2486,6 +3666,10 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" + }, "dependencies": { "abbrev": { "version": "1.1.0", @@ -2495,7 +3679,11 @@ "ajv": { "version": "4.11.8", "bundled": true, - "optional": true + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } }, "ansi-regex": { "version": "2.1.1", @@ -2509,7 +3697,11 @@ "are-we-there-yet": { "version": "1.1.4", "bundled": true, - "optional": true + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } }, "asn1": { "version": "0.2.3", @@ -2543,19 +3735,32 @@ "bcrypt-pbkdf": { "version": "1.0.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } }, "block-stream": { "version": "0.0.9", - "bundled": true + "bundled": true, + "requires": { + "inherits": "2.0.3" + } }, "boom": { "version": "2.10.1", - "bundled": true + "bundled": true, + "requires": { + "hoek": "2.16.3" + } }, "brace-expansion": { "version": "1.1.7", - "bundled": true + "bundled": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } }, "buffer-shims": { "version": "1.0.0", @@ -2577,7 +3782,10 @@ }, "combined-stream": { "version": "1.0.5", - "bundled": true + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } }, "concat-map": { "version": "0.0.1", @@ -2593,12 +3801,18 @@ }, "cryptiles": { "version": "2.0.5", - "bundled": true + "bundled": true, + "requires": { + "boom": "2.10.1" + } }, "dashdash": { "version": "1.14.1", "bundled": true, "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -2610,7 +3824,10 @@ "debug": { "version": "2.6.8", "bundled": true, - "optional": true + "optional": true, + "requires": { + "ms": "2.0.0" + } }, "deep-extend": { "version": "0.4.2", @@ -2634,7 +3851,10 @@ "ecc-jsbn": { "version": "0.1.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.1" + } }, "extend": { "version": "3.0.1", @@ -2653,7 +3873,12 @@ "form-data": { "version": "2.1.4", "bundled": true, - "optional": true + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } }, "fs.realpath": { "version": "1.0.0", @@ -2661,22 +3886,46 @@ }, "fstream": { "version": "1.0.11", - "bundled": true + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } }, "fstream-ignore": { "version": "1.0.5", "bundled": true, - "optional": true + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } }, "gauge": { "version": "2.7.4", "bundled": true, - "optional": true + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } }, "getpass": { "version": "0.1.7", "bundled": true, "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -2687,7 +3936,15 @@ }, "glob": { "version": "7.1.2", - "bundled": true + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "graceful-fs": { "version": "4.1.11", @@ -2701,7 +3958,11 @@ "har-validator": { "version": "4.2.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } }, "has-unicode": { "version": "2.0.1", @@ -2710,7 +3971,13 @@ }, "hawk": { "version": "3.1.3", - "bundled": true + "bundled": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } }, "hoek": { "version": "2.16.3", @@ -2719,11 +3986,20 @@ "http-signature": { "version": "1.1.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } }, "inflight": { "version": "1.0.6", - "bundled": true + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } }, "inherits": { "version": "2.0.3", @@ -2736,7 +4012,10 @@ }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } }, "is-typedarray": { "version": "1.0.0", @@ -2755,7 +4034,10 @@ "jodid25519": { "version": "1.0.2", "bundled": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.1" + } }, "jsbn": { "version": "0.1.1", @@ -2770,7 +4052,10 @@ "json-stable-stringify": { "version": "1.0.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "jsonify": "0.0.0" + } }, "json-stringify-safe": { "version": "5.0.1", @@ -2786,6 +4071,12 @@ "version": "1.4.0", "bundled": true, "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -2800,11 +4091,17 @@ }, "mime-types": { "version": "2.1.15", - "bundled": true + "bundled": true, + "requires": { + "mime-db": "1.27.0" + } }, "minimatch": { "version": "3.0.4", - "bundled": true + "bundled": true, + "requires": { + "brace-expansion": "1.1.7" + } }, "minimist": { "version": "0.0.8", @@ -2812,7 +4109,10 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true + "bundled": true, + "requires": { + "minimist": "0.0.8" + } }, "ms": { "version": "2.0.0", @@ -2822,17 +4122,40 @@ "node-pre-gyp": { "version": "0.6.39", "bundled": true, - "optional": true + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } }, "nopt": { "version": "4.0.1", "bundled": true, - "optional": true + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } }, "npmlog": { "version": "4.1.0", "bundled": true, - "optional": true + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } }, "number-is-nan": { "version": "1.0.1", @@ -2850,7 +4173,10 @@ }, "once": { "version": "1.4.0", - "bundled": true + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } }, "os-homedir": { "version": "1.0.2", @@ -2865,7 +4191,11 @@ "osenv": { "version": "0.1.4", "bundled": true, - "optional": true + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } }, "path-is-absolute": { "version": "1.0.1", @@ -2894,6 +4224,12 @@ "version": "1.2.1", "bundled": true, "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, "dependencies": { "minimist": { "version": "1.2.0", @@ -2904,16 +4240,52 @@ }, "readable-stream": { "version": "2.2.9", - "bundled": true + "bundled": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } }, "request": { "version": "2.81.0", "bundled": true, - "optional": true + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } }, "rimraf": { "version": "2.6.1", - "bundled": true + "bundled": true, + "requires": { + "glob": "7.1.2" + } }, "safe-buffer": { "version": "5.0.1", @@ -2936,12 +4308,26 @@ }, "sntp": { "version": "1.0.9", - "bundled": true + "bundled": true, + "requires": { + "hoek": "2.16.3" + } }, "sshpk": { "version": "1.13.0", "bundled": true, "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -2950,13 +4336,21 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true - }, "string-width": { "version": "1.0.2", - "bundled": true + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "requires": { + "safe-buffer": "5.0.1" + } }, "stringstream": { "version": "0.0.5", @@ -2965,7 +4359,10 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } }, "strip-json-comments": { "version": "2.0.1", @@ -2974,22 +4371,43 @@ }, "tar": { "version": "2.2.1", - "bundled": true + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } }, "tar-pack": { "version": "3.4.0", "bundled": true, - "optional": true + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } }, "tough-cookie": { "version": "2.3.2", "bundled": true, - "optional": true + "optional": true, + "requires": { + "punycode": "1.4.1" + } }, "tunnel-agent": { "version": "0.6.0", "bundled": true, - "optional": true + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } }, "tweetnacl": { "version": "0.14.5", @@ -3013,12 +4431,18 @@ "verror": { "version": "1.3.6", "bundled": true, - "optional": true + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } }, "wide-align": { "version": "1.1.2", "bundled": true, - "optional": true + "optional": true, + "requires": { + "string-width": "1.0.2" + } }, "wrappy": { "version": "1.0.2", @@ -3031,17 +4455,16 @@ "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", "dev": true }, - "function.name-polyfill": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/function.name-polyfill/-/function.name-polyfill-1.0.5.tgz", - "integrity": "sha1-00m7TiSjJPCBIEVe54oEFCsSV7s=", - "dev": true - }, "function.prototype.name": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.0.3.tgz", "integrity": "sha512-5EblxZUdioXi2JiMZ9FUbwYj40eQ9MFHyzFLBSPdlRl3SO8l7SLWuAnQ/at/1Wi4hjJwME/C5WpF2ZfAc8nGNw==", - "dev": true + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "is-callable": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz" + } }, "fuse.js": { "version": "3.0.5", @@ -3056,7 +4479,10 @@ "generate-object-property": { "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true + "dev": true, + "requires": { + "is-property": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" + } }, "get-caller-file": { "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -3072,6 +4498,9 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -3081,37 +4510,42 @@ } } }, - "github-slugger": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.1.3.tgz", - "integrity": "sha1-MUpudZoYwrDMV2DVEsy6tUnFSac=", - "dev": true, - "dependencies": { - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - } - } - }, "glob": { "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "dev": true + "dev": true, + "requires": { + "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } }, "glob-base": { "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } }, "glob-parent": { "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=" + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } }, "global": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "2.19.0", + "process": "0.5.2" + }, "dependencies": { "process": { "version": "0.5.2", @@ -3128,18 +4562,37 @@ "globby": { "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true - }, - "glogg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", - "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", - "dev": true + "dev": true, + "requires": { + "array-union": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "arrify": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } }, "got": { "version": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", - "dev": true + "dev": true, + "requires": { + "create-error-class": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "duplexer2": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "is-redirect": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "is-retry-allowed": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "lowercase-keys": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "node-status-codes": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "parse-json": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "read-all-stream": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "readable-stream": "2.3.2", + "timed-out": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "unzip-response": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "url-parse-lax": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" + } }, "graceful-fs": { "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -3154,7 +4607,10 @@ "gzip-size": { "version": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", - "dev": true + "dev": true, + "requires": { + "duplexer": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" + } }, "handle-thing": { "version": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", @@ -3166,6 +4622,12 @@ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, "dependencies": { "async": { "version": "1.5.2", @@ -3177,7 +4639,10 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true + "dev": true, + "requires": { + "amdefine": "1.0.1" + } }, "uglify-js": { "version": "2.8.29", @@ -3185,6 +4650,11 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "optional": true, + "requires": { + "source-map": "0.5.6", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "3.10.0" + }, "dependencies": { "source-map": { "version": "0.5.6", @@ -3200,7 +4670,13 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, - "optional": true + "optional": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + } } } }, @@ -3214,17 +4690,27 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true + "dev": true, + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "har-schema": "1.0.5" + } }, "has": { "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true + "dev": true, + "requires": { + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" + } }, "has-ansi": { "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } }, "has-flag": { "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", @@ -3233,18 +4719,31 @@ "hash-base": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=" + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } }, "hash.js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==" + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } }, "hawk": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } }, "he": { "version": "1.1.1", @@ -3252,21 +4751,27 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "highlight.js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", - "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", - "dev": true - }, "history": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/history/-/history-4.6.3.tgz", - "integrity": "sha1-bXI6hxLFgda+836MJvSu3G64aWc=" + "integrity": "sha1-bXI6hxLFgda+836MJvSu3G64aWc=", + "requires": { + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "resolve-pathname": "2.1.0", + "value-equal": "0.2.1", + "warning": "3.0.0" + } }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "minimalistic-crypto-utils": "1.0.1" + } }, "hoek": { "version": "2.16.3", @@ -3283,7 +4788,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true + "dev": true, + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } }, "hosted-git-info": { "version": "2.5.0", @@ -3293,7 +4802,13 @@ "hpack.js": { "version": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "obuf": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "readable-stream": "2.3.2", + "wbuf": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz" + } }, "html-comment-regex": { "version": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", @@ -3304,7 +4819,10 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz", "integrity": "sha1-eb96eF6klf5mFl5zQVPzY/9UN9o=", - "dev": true + "dev": true, + "requires": { + "whatwg-encoding": "1.0.1" + } }, "html-entities": { "version": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", @@ -3315,17 +4833,41 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.3.tgz", "integrity": "sha512-iKRzQQDuTCsq0Ultbi/mfJJnR0D3AdZKTq966Gsp92xkmAPCV4Xi08qhJ0Dl3ZAWemSgJ7qZK+UsZc0gFqK6wg==", - "dev": true + "dev": true, + "requires": { + "camel-case": "3.0.0", + "clean-css": "4.1.7", + "commander": "2.11.0", + "he": "1.1.1", + "ncname": "1.0.0", + "param-case": "2.1.1", + "relateurl": "0.2.7", + "uglify-js": "3.0.27" + } }, "html-webpack-plugin": { "version": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", "integrity": "sha1-LnhjtX5f1I/iYzA+L/yTTDBk0Ak=", "dev": true, + "requires": { + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "html-minifier": "3.5.3", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "lodash": "4.17.4", + "pretty-error": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "toposort": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz" + }, "dependencies": { "loader-utils": { "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true + "dev": true, + "requires": { + "big.js": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "emojis-list": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } } } }, @@ -3333,16 +4875,31 @@ "version": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "domhandler": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "domutils": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, "dependencies": { "domutils": { "version": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true + "dev": true, + "requires": { + "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } }, "readable-stream": { "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true + "dev": true, + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "0.0.1", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } }, "string_decoder": { "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -3360,17 +4917,33 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } }, "http-proxy": { "version": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", - "dev": true + "dev": true, + "requires": { + "eventemitter3": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + } }, "http-proxy-middleware": { "version": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", "dev": true, + "requires": { + "http-proxy": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "lodash": "4.17.4", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz" + }, "dependencies": { "is-extglob": { "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3380,7 +4953,10 @@ "is-glob": { "version": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true + "dev": true, + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + } } } }, @@ -3388,33 +4964,26 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.1" + } }, "https-browserify": { "version": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" }, - "hyphenate-style-name": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", - "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" - }, - "iconv-lite": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", - "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + "get-caller-file": { + "version": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" }, "icss-replace-symbols": { "version": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "dev": true }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true - }, "ieee754": { "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" @@ -3424,15 +4993,17 @@ "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", "dev": true }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + "glob-base": { + "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" }, "immutability-helper": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.4.0.tgz", - "integrity": "sha512-rW/L/56ZMo9NStMK85kFrUFFGy4NeJbCdhfrDHIZrFfxYtuwuxD+dT3mWMcdmrNO61hllc60AeGglCRhfZ1dZw==" + "integrity": "sha512-rW/L/56ZMo9NStMK85kFrUFFGy4NeJbCdhfrDHIZrFfxYtuwuxD+dT3mWMcdmrNO61hllc60AeGglCRhfZ1dZw==", + "requires": { + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz" + } }, "imurmurhash": { "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3442,7 +5013,10 @@ "indent-string": { "version": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true + "dev": true, + "requires": { + "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + } }, "indexes-of": { "version": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -3456,7 +5030,11 @@ "inflight": { "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true + "dev": true, + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } }, "inherits": { "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -3470,17 +5048,40 @@ "inline-process-browser": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", - "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" + "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=", + "requires": { + "falafel": "1.2.0", + "through2": "0.6.5" + } }, "inline-style-prefixer": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz", - "integrity": "sha1-DMyS5ZAv5uDSjZdcQlhEP4gGFfg=" + "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.8.1", + "css-in-js-utils": "2.0.0" + } }, "inquirer": { "version": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true + "dev": true, + "requires": { + "ansi-escapes": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "cli-cursor": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "cli-width": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "figures": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "lodash": "4.17.4", + "readline2": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "run-async": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "rx-lite": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } }, "interpret": { "version": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", @@ -3488,7 +5089,10 @@ }, "invariant": { "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=" + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "requires": { + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + } }, "invert-kv": { "version": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -3505,31 +5109,16 @@ "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", "dev": true }, - "is-alphabetical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", - "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", - "dev": true - }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", - "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", - "dev": true - }, "is-arrayish": { "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-binary-path": { "version": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=" + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "1.9.0" + } }, "is-buffer": { "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", @@ -3537,7 +5126,10 @@ }, "is-builtin-module": { "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" + } }, "is-callable": { "version": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", @@ -3548,7 +5140,10 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", - "dev": true + "dev": true, + "requires": { + "ci-info": "1.0.0" + } }, "is-date-object": { "version": "1.0.1", @@ -3556,12 +5151,6 @@ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, - "is-decimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", - "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", - "dev": true - }, "is-directory": { "version": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", @@ -3573,7 +5162,10 @@ }, "is-equal-shallow": { "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=" + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } }, "is-extendable": { "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3586,32 +5178,35 @@ "is-finite": { "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true + "dev": true, + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } }, "is-fullwidth-code-point": { "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=" + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } }, "is-glob": { "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=" - }, - "is-hexadecimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", - "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", - "dev": true - }, - "is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", - "dev": true + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } }, "is-my-json-valid": { "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", - "dev": true + "dev": true, + "requires": { + "generate-function": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "generate-object-property": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "jsonpointer": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } }, "is-npm": { "version": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -3620,7 +5215,10 @@ }, "is-number": { "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=" + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + } }, "is-obj": { "version": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -3635,12 +5233,18 @@ "is-path-in-cwd": { "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "dev": true + "dev": true, + "requires": { + "is-path-inside": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz" + } }, "is-path-inside": { "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", - "dev": true + "dev": true, + "requires": { + "path-is-inside": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + } }, "is-plain-obj": { "version": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -3655,11 +5259,10 @@ "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" }, - "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 + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" }, "is-property": { "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -3675,67 +5278,60 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true + "dev": true, + "requires": { + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + } }, "is-resolvable": { "version": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", - "dev": true + "dev": true, + "requires": { + "tryit": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz" + } }, "is-retry-allowed": { "version": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", "dev": true }, - "is-root": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", - "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "indexes-of": { + "version": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "dev": true }, "is-stream": { "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true }, "is-svg": { "version": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true + "dev": true, + "requires": { + "html-comment-regex": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz" + } }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", "dev": true }, - "is-typedarray": { + "inline-process-browser": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", + "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" }, "is-utf8": { "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, - "is-whitespace-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", - "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs=", - "dev": true - }, - "is-word-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz", - "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs=", - "dev": true - }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -3756,6 +5352,9 @@ "isobject": { "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + }, "dependencies": { "isarray": { "version": "1.0.0", @@ -3767,7 +5366,11 @@ "isomorphic-fetch": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=" + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "1.7.2", + "whatwg-fetch": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz" + } }, "isstream": { "version": "0.1.2", @@ -3780,12 +5383,34 @@ "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.11.tgz", "integrity": "sha1-/MC0YeKzvaceMFFVE4I4doJX2d4=", "dev": true, + "requires": { + "async": "2.5.0", + "fileset": "2.0.3", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "istanbul-lib-hook": "1.0.7", + "istanbul-lib-instrument": "1.7.4", + "istanbul-lib-report": "1.1.1", + "istanbul-lib-source-maps": "1.2.1", + "istanbul-reports": "1.1.1", + "js-yaml": "3.9.1", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, "dependencies": { "istanbul-lib-instrument": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz", "integrity": "sha1-6f2SDkdn89Ge3HZeLWs/XMvQ7qg=", - "dev": true + "dev": true, + "requires": { + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "semver": "5.4.1" + } } } }, @@ -3798,37 +5423,68 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz", "integrity": "sha512-3U2HB9y1ZV9UmFlE12Fx+nPtFqIymzrqCksrXujm3NVbAZIJg/RfYgO1XiIa0mbmxTjWpVEVlkIZJ25xVIAfkQ==", - "dev": true + "dev": true, + "requires": { + "append-transform": "0.4.0" + } }, "istanbul-lib-instrument": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz", "integrity": "sha1-6f2SDkdn89Ge3HZeLWs/XMvQ7qg=", - "dev": true + "dev": true, + "requires": { + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "semver": "5.4.1" + } }, "istanbul-lib-report": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", "integrity": "sha512-tvF+YmCmH4thnez6JFX06ujIA19WPa9YUiwjc1uALF2cv5dmE3It8b5I8Ob7FHJ70H9Y5yF+TDkVa/mcADuw1Q==", - "dev": true + "dev": true, + "requires": { + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "path-parse": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } }, "istanbul-lib-source-maps": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz", "integrity": "sha512-mukVvSXCn9JQvdJl8wP/iPhqig0MRtuWuD4ZNKo6vB2Ik//AmhAKe3QnPN02dmkRe3lTudFk3rzoHhwU4hb94w==", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "istanbul-reports": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.1.tgz", "integrity": "sha512-P8G873A0kW24XRlxHVGhMJBhQ8gWAec+dae7ZxOBzxT4w+a9ATSPvRVK3LB1RAJ9S8bg2tOyWHAGW40Zd2dKfw==", - "dev": true + "dev": true, + "requires": { + "handlebars": "4.0.10" + } }, "jest": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.4.tgz", "integrity": "sha1-PdJgwpidba1nix6cxNkZRPbWAqw=", "dev": true, + "requires": { + "jest-cli": "20.0.4" + }, "dependencies": { "callsites": { "version": "2.0.0", @@ -3840,7 +5496,39 @@ "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", - "dev": true + "dev": true, + "requires": { + "ansi-escapes": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "callsites": "2.0.0", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "is-ci": "1.0.10", + "istanbul-api": "1.1.11", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "istanbul-lib-instrument": "1.7.4", + "istanbul-lib-source-maps": "1.2.1", + "jest-changed-files": "20.0.3", + "jest-config": "20.0.4", + "jest-docblock": "20.0.3", + "jest-environment-jsdom": "20.0.3", + "jest-haste-map": "20.0.4", + "jest-jasmine2": "20.0.4", + "jest-message-util": "20.0.3", + "jest-regex-util": "20.0.3", + "jest-resolve-dependencies": "20.0.3", + "jest-runtime": "20.0.4", + "jest-snapshot": "20.0.3", + "jest-util": "20.0.3", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "node-notifier": "5.1.2", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "string-length": "1.0.1", + "throat": "3.2.0", + "which": "1.3.0", + "worker-farm": "1.4.1", + "yargs": "7.1.0" + } } } }, @@ -3854,13 +5542,31 @@ "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.4.tgz", "integrity": "sha1-43kwqyIXyRNgXv8T5712PsSPruo=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "jest-environment-jsdom": "20.0.3", + "jest-environment-node": "20.0.3", + "jest-jasmine2": "20.0.4", + "jest-matcher-utils": "20.0.3", + "jest-regex-util": "20.0.3", + "jest-resolve": "20.0.4", + "jest-validate": "20.0.3", + "pretty-format": "20.0.3" + } }, "jest-diff": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-20.0.3.tgz", "integrity": "sha1-gfKI/Z5nXw+yPHXxwrGURf5YZhc=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "diff": "3.3.0", + "jest-matcher-utils": "20.0.3", + "pretty-format": "20.0.3" + } }, "jest-docblock": { "version": "20.0.3", @@ -3872,49 +5578,95 @@ "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz", "integrity": "sha1-BIqKwS7iJfcZBBdxODS7mZeH3pk=", - "dev": true + "dev": true, + "requires": { + "jest-mock": "20.0.3", + "jest-util": "20.0.3", + "jsdom": "9.12.0" + } }, "jest-environment-node": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-20.0.3.tgz", "integrity": "sha1-1Ii8RhKvLCRumG6K52caCZFj1AM=", - "dev": true + "dev": true, + "requires": { + "jest-mock": "20.0.3", + "jest-util": "20.0.3" + } }, "jest-haste-map": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-20.0.4.tgz", "integrity": "sha1-ZT61XIic48Ah97lGk/IKQVm63wM=", - "dev": true + "dev": true, + "requires": { + "fb-watchman": "2.0.0", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jest-docblock": "20.0.3", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "sane": "1.6.0", + "worker-farm": "1.4.1" + } }, "jest-jasmine2": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz", "integrity": "sha1-/MWxQReA2RHQQpAu8YWehS5g1eE=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jest-diff": "20.0.3", + "jest-matcher-utils": "20.0.3", + "jest-matchers": "20.0.3", + "jest-message-util": "20.0.3", + "jest-snapshot": "20.0.3", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "p-map": "1.1.1" + } }, "jest-junit-reporter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jest-junit-reporter/-/jest-junit-reporter-1.1.0.tgz", "integrity": "sha1-iNYAbsE/gt9AxHiCyGQJic3LFDQ=", - "dev": true + "dev": true, + "requires": { + "xml": "1.0.1" + } }, "jest-matcher-utils": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz", "integrity": "sha1-s6a443yld4A7CDKpixZPRLeBVhI=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "pretty-format": "20.0.3" + } }, "jest-matchers": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-matchers/-/jest-matchers-20.0.3.tgz", "integrity": "sha1-ymnbHDLbWm9wf6XgQBq7VXAN/WA=", - "dev": true + "dev": true, + "requires": { + "jest-diff": "20.0.3", + "jest-matcher-utils": "20.0.3", + "jest-message-util": "20.0.3", + "jest-regex-util": "20.0.3" + } }, "jest-message-util": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-20.0.3.tgz", "integrity": "sha1-auwoRDBvyw5udNV5bBAG2W/dgxw=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" + } }, "jest-mock": { "version": "20.0.3", @@ -3932,19 +5684,44 @@ "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-20.0.4.tgz", "integrity": "sha1-lEiz6La6/BVHlETGSZBFt//ll6U=", - "dev": true + "dev": true, + "requires": { + "browser-resolve": "1.11.2", + "is-builtin-module": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolve": "1.4.0" + } }, "jest-resolve-dependencies": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz", "integrity": "sha1-bhSntxevDyyzZnxUneQK8Bexcjo=", - "dev": true + "dev": true, + "requires": { + "jest-regex-util": "20.0.3" + } }, "jest-runtime": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-20.0.4.tgz", "integrity": "sha1-osgCIZxCA/dU3xQE5JAYYWnRJNg=", "dev": true, + "requires": { + "babel-core": "6.25.0", + "babel-jest": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", + "babel-plugin-istanbul": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jest-config": "20.0.4", + "jest-haste-map": "20.0.4", + "jest-regex-util": "20.0.3", + "jest-resolve": "20.0.4", + "jest-util": "20.0.3", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "strip-bom": "3.0.0", + "yargs": "7.1.0" + }, "dependencies": { "strip-bom": { "version": "3.0.0", @@ -3958,19 +5735,42 @@ "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-20.0.3.tgz", "integrity": "sha1-W4R+GtsaTZCFKn+fElCG4YfHZWY=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "jest-diff": "20.0.3", + "jest-matcher-utils": "20.0.3", + "jest-util": "20.0.3", + "natural-compare": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "pretty-format": "20.0.3" + } }, "jest-util": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-20.0.3.tgz", "integrity": "sha1-DAf32A2C9OWmfG+LnD/n9lz9Mq0=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jest-message-util": "20.0.3", + "jest-mock": "20.0.3", + "jest-validate": "20.0.3", + "leven": "2.1.0", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } }, "jest-validate": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-20.0.3.tgz", "integrity": "sha1-0M/R3k9XnymEhJJcKA+PHZTsPKs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "jest-matcher-utils": "20.0.3", + "leven": "2.1.0", + "pretty-format": "20.0.3" + } }, "js-base64": { "version": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", @@ -3991,7 +5791,11 @@ "version": "3.9.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz", "integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==", - "dev": true + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } }, "jsbn": { "version": "0.1.1", @@ -4011,6 +5815,27 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", "integrity": "sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=", "dev": true, + "requires": { + "abab": "1.0.3", + "acorn": "4.0.13", + "acorn-globals": "3.1.0", + "array-equal": "1.0.0", + "content-type-parser": "1.0.1", + "cssom": "0.3.2", + "cssstyle": "0.2.37", + "escodegen": "1.8.1", + "html-encoding-sniffer": "1.0.1", + "nwmatcher": "1.4.1", + "parse5": "1.5.1", + "request": "2.81.0", + "sax": "1.2.4", + "symbol-tree": "3.2.2", + "tough-cookie": "2.3.2", + "webidl-conversions": "4.0.1", + "whatwg-encoding": "1.0.1", + "whatwg-url": "4.8.0", + "xml-name-validator": "2.0.1" + }, "dependencies": { "acorn": { "version": "4.0.13", @@ -4043,7 +5868,10 @@ }, "json-stable-stringify": { "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } }, "json-stringify-safe": { "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -4062,7 +5890,10 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } }, "jsonify": { "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -4078,6 +5909,12 @@ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -4087,52 +5924,15 @@ } } }, - "jss": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-8.1.0.tgz", - "integrity": "sha512-NZ4CNAoPaPlM2rqHxPG5uGQbQEFZ9n1PITn0+wGIdAk2ZtA/F6el0SphLHf8So1Sx6N34hnVFFIuc32/hdsEzw==", - "dev": true - }, - "jss-camel-case": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jss-camel-case/-/jss-camel-case-5.0.0.tgz", - "integrity": "sha512-vz11ip5EIlGuevtlUo9xIgiuD+it4Ebbb0+Y4o0A4oA8eOWY4aY7ihi/L7WvkQ54xnGOjUvLZ6nm2VYch2ufYg==", - "dev": true - }, - "jss-compose": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jss-compose/-/jss-compose-4.0.0.tgz", - "integrity": "sha512-VnsEziD2Lwrfwp10wx39FNybRLW5+RX/E2qQAXPAMbS+nHc0Jf2dC6ZiCfn5FaBGrpzLfIZ9MalTJDx4CQoMAQ==", - "dev": true - }, - "jss-default-unit": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/jss-default-unit/-/jss-default-unit-7.0.0.tgz", - "integrity": "sha512-U1Oi1h45vFRuISr+g1DQ3Oua7CkNKNs47fTdiT/lHkuBMc6BBDUbPv9IbPPhk9gsEaX45Iy9TX8CAuaHLPCfEA==", - "dev": true - }, - "jss-global": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jss-global/-/jss-global-2.0.0.tgz", - "integrity": "sha512-/FSOMp4lF/vg47T/w8kKvL9tu7ka9am8N4izS63W81Qlay9hAq6xe9RxrPxygLpnn4KEb8LNbkKRoUv4SJfQsQ==", - "dev": true - }, - "jss-isolate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jss-isolate/-/jss-isolate-4.0.0.tgz", - "integrity": "sha512-bVvWcQj+Jeso8yGcYqnw/h5klf8+ur3Ytr7GG4JQj9TDhueJSvxctoATdfbcZiX72897xvdsDE0OjKUYuilNkQ==", - "dev": true - }, - "jss-nested": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jss-nested/-/jss-nested-5.0.0.tgz", - "integrity": "sha512-9Molau+XVpSc6QEco3EC5yXmzeGMc5ZVII8+qy6jD6bvu6Y9mpfGoJ00LalR/n7xr/LC7Cxgs44UQQlLzumMBg==", - "dev": true - }, "jstransform": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", + "requires": { + "base62": "0.1.1", + "esprima-fb": "3001.1.0-dev-harmony-fb", + "source-map": "0.1.31" + }, "dependencies": { "esprima-fb": { "version": "3001.1.0-dev-harmony-fb", @@ -4142,7 +5942,10 @@ "source-map": { "version": "0.1.31", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", - "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=" + "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=", + "requires": { + "amdefine": "1.0.1" + } } } }, @@ -4158,17 +5961,26 @@ }, "kind-of": { "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } }, "klaw": { "version": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } }, "latest-version": { "version": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=", - "dev": true + "dev": true, + "requires": { + "package-json": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz" + } }, "lazy-cache": { "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -4181,7 +5993,10 @@ }, "lcid": { "version": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=" + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz" + } }, "leven": { "version": "2.1.0", @@ -4192,27 +6007,42 @@ "levn": { "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true + "dev": true, + "requires": { + "prelude-ls": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "type-check": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + } }, "lie": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lie/-/lie-3.0.2.tgz", - "integrity": "sha1-/9oh17uibzd8rYZdNkmy/Izjn+o=" - }, - "listify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/listify/-/listify-1.0.0.tgz", - "integrity": "sha1-A8p7otFQ1CZ3c/dOV1WNEFPSvuM=", - "dev": true + "integrity": "sha1-/9oh17uibzd8rYZdNkmy/Izjn+o=", + "requires": { + "es3ify": "0.1.4", + "immediate": "3.0.6", + "inline-process-browser": "1.0.0", + "unreachable-branch-transform": "0.3.0" + } }, "load-json-file": { "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=" + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "parse-json": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + } }, "loader-fs-cache": { "version": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", - "dev": true + "dev": true, + "requires": { + "find-cache-dir": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } }, "loader-runner": { "version": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", @@ -4220,18 +6050,30 @@ }, "loader-utils": { "version": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "emojis-list": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz" + } }, "localforage": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.5.0.tgz", - "integrity": "sha1-a5lOGbVmEfqF3zmS3zl6xKtm6BU=" + "integrity": "sha1-a5lOGbVmEfqF3zmS3zl6xKtm6BU=", + "requires": { + "lie": "3.0.2" + } }, "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" + }, "dependencies": { "path-exists": { "version": "3.0.0", @@ -4277,12 +6119,6 @@ "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "lodash.defaults": { "version": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", @@ -4306,23 +6142,11 @@ "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "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.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true - }, "lodash.isfinite": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.2.0.tgz", @@ -4371,12 +6195,19 @@ "lodash.template": { "version": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true + "dev": true, + "requires": { + "lodash._reinterpolate": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "lodash.templatesettings": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz" + } }, "lodash.templatesettings": { "version": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true + "dev": true, + "requires": { + "lodash._reinterpolate": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" + } }, "lodash.throttle": { "version": "4.1.1", @@ -4392,20 +6223,21 @@ "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, - "longest-streak": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.1.tgz", - "integrity": "sha1-QtKRtUEeQDZcAOYxk0l+IkcxbjU=", - "dev": true - }, "loose-envify": { "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=" + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "3.0.2" + } }, "loud-rejection": { "version": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true + "dev": true, + "requires": { + "currently-unhandled": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "signal-exit": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + } }, "lower-case": { "version": "1.1.4", @@ -4422,75 +6254,80 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } }, "macaddress": { "version": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", "dev": true }, - "magic-string": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.14.0.tgz", - "integrity": "sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI=", - "dev": true - }, "make-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", - "dev": true + "dev": true, + "requires": { + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + } }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true + "dev": true, + "requires": { + "tmpl": "1.0.4" + } }, "map-obj": { "version": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, - "markdown-escapes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", - "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=", - "dev": true - }, - "markdown-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.1.tgz", - "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", - "dev": true - }, - "markdown-to-jsx": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-5.4.1.tgz", - "integrity": "sha512-C1bOuwvZ36MKE+c0m0SH98Dv4xCvmXxL+TvOyXL5q6NklnlAMJ9Mn94kAA6OpFnUCKiZf/vhPF+GOupkKZjKVg==", - "dev": true - }, "material-ui": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.19.0.tgz", - "integrity": "sha1-l4HdWtNAr6BSAwke4fb7jTKlq0E=" + "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.25.0", + "inline-style-prefixer": "3.0.8", + "keycode": "2.1.9", + "lodash.merge": "4.6.0", + "lodash.throttle": "4.1.1", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "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.3", + "resolved": "https://registry.npmjs.org/material-ui-chip-input/-/material-ui-chip-input-0.18.3.tgz", + "integrity": "sha512-tWmsPIAhTeJlHFiR653UyiLHI0CDfCqhuDyM+4ldI/awCOVxSW+6NZcUWD3CIsgNlCEso0CPB09lynvuNCBkhw==", + "requires": { + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "material-ui-superselectfield": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/material-ui-superselectfield/-/material-ui-superselectfield-1.5.6.tgz", - "integrity": "sha512-45i+o+Tq+PAUv1CaodR+u/unGpUJroIQDuHQT7NuODLrOcscWj1JDH2o2rkEkH9qSczZ9f5Plq8v4Lj++wqALw==" + "integrity": "sha512-45i+o+Tq+PAUv1CaodR+u/unGpUJroIQDuHQT7NuODLrOcscWj1JDH2o2rkEkH9qSczZ9f5Plq8v4Lj++wqALw==", + "requires": { + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "react-infinite": "0.12.1" + } }, "math-expression-evaluator": { "version": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, - "mdast-util-compact": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", - "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", - "dev": true - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4499,12 +6336,28 @@ }, "memory-fs": { "version": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "readable-stream": "2.3.2" + } }, "meow": { "version": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, + "requires": { + "camelcase-keys": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "loud-rejection": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "map-obj": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "normalize-package-data": "2.4.0", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "redent": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "trim-newlines": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" + }, "dependencies": { "minimist": { "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -4533,12 +6386,31 @@ }, "micromatch": { "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=" + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "braces": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "expand-brackets": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "extglob": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "filename-regex": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "object.omit": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "parse-glob": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "regex-cache": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz" + } }, "miller-rabin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", - "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=" + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } }, "mime": { "version": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", @@ -4555,7 +6427,10 @@ "version": "2.1.16", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", - "dev": true + "dev": true, + "requires": { + "mime-db": "1.29.0" + } }, "mimic-fn": { "version": "1.1.0", @@ -4566,7 +6441,10 @@ "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=" + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "0.1.1" + } }, "minimalistic-assert": { "version": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -4579,7 +6457,10 @@ }, "minimatch": { "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + } }, "minimist": { "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", @@ -4587,7 +6468,10 @@ }, "mkdirp": { "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } }, "ms": { "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -4613,7 +6497,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", - "dev": true + "dev": true, + "requires": { + "xml-char-classes": "1.0.0" + } }, "negotiator": { "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -4624,18 +6511,19 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=", - "dev": true - }, - "node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=", - "dev": true + "dev": true, + "requires": { + "lower-case": "1.1.4" + } }, "node-fetch": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz", - "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==" + "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==", + "requires": { + "encoding": "0.1.12", + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + } }, "node-int64": { "version": "0.4.0", @@ -4646,6 +6534,31 @@ "node-libs-browser": { "version": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", + "requires": { + "assert": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "browserify-zlib": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "buffer": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "console-browserify": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "constants-browserify": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "crypto-browserify": "3.11.1", + "domain-browser": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "events": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "https-browserify": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "os-browserify": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "path-browserify": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "process": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "querystring-es3": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "readable-stream": "2.3.2", + "stream-browserify": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "stream-http": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "timers-browserify": "2.0.3", + "tty-browserify": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "url": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "util": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "vm-browserify": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz" + }, "dependencies": { "string_decoder": { "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -4657,7 +6570,13 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.1.2.tgz", "integrity": "sha1-L6nhJgX6EACdRFSdb82KY93g5P8=", - "dev": true + "dev": true, + "requires": { + "growly": "1.3.0", + "semver": "5.4.1", + "shellwords": "0.1.0", + "which": "1.3.0" + } }, "node-status-codes": { "version": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", @@ -4667,11 +6586,20 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==" + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } }, "normalize-path": { "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=" + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz" + } }, "normalize-range": { "version": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -4681,12 +6609,21 @@ "normalize-url": { "version": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true + "dev": true, + "requires": { + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "prepend-http": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "query-string": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "sort-keys": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" + } }, "nth-check": { "version": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true + "dev": true, + "requires": { + "boolbase": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + } }, "num2fraction": { "version": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -4732,23 +6669,44 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", - "dev": true + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "object-keys": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + } }, "object.entries": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", - "dev": true + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "es-abstract": "1.8.0", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + } }, "object.omit": { "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=" + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + } }, "object.values": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", - "dev": true + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "es-abstract": "1.8.0", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + } }, "obuf": { "version": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", @@ -4759,7 +6717,10 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true + "dev": true, + "requires": { + "ee-first": "1.1.1" + } }, "on-headers": { "version": "1.0.1", @@ -4770,19 +6731,16 @@ "once": { "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true + "dev": true, + "requires": { + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } }, "onetime": { "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, - "open": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", - "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=", - "dev": true - }, "openseadragon": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/openseadragon/-/openseadragon-2.3.1.tgz", @@ -4792,18 +6750,33 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", - "dev": true + "dev": true, + "requires": { + "is-wsl": "1.1.0" + } }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true + "dev": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "wordwrap": "0.0.2" + } }, "optionator": { "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, + "requires": { + "deep-is": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "fast-levenshtein": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "levn": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "prelude-ls": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "type-check": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "wordwrap": "1.0.0" + }, "dependencies": { "wordwrap": { "version": "1.0.0", @@ -4817,11 +6790,18 @@ "version": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", "dev": true, + "requires": { + "url-parse": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz" + }, "dependencies": { "url-parse": { "version": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", - "dev": true + "dev": true, + "requires": { + "querystringify": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + } } } }, @@ -4836,7 +6816,10 @@ }, "os-locale": { "version": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=" + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" + } }, "os-tmpdir": { "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -4846,7 +6829,11 @@ "osenv": { "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true + "dev": true, + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } }, "p-limit": { "version": "1.1.0", @@ -4858,7 +6845,10 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true + "dev": true, + "requires": { + "p-limit": "1.1.0" + } }, "p-map": { "version": "1.1.1", @@ -4869,7 +6859,13 @@ "package-json": { "version": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", - "dev": true + "dev": true, + "requires": { + "got": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "registry-auth-token": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", + "registry-url": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "semver": "5.4.1" + } }, "pako": { "version": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -4884,26 +6880,39 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true + "dev": true, + "requires": { + "no-case": "2.3.1" + } }, "parse-asn1": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=" - }, - "parse-entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", - "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=", - "dev": true + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "requires": { + "asn1.js": "4.9.1", + "browserify-aes": "1.0.6", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.0", + "pbkdf2": "3.0.13" + } }, "parse-glob": { "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=" + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "is-dotfile": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } }, "parse-json": { "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=" + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz" + } }, "parse5": { "version": "1.5.1", @@ -4923,7 +6932,10 @@ "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=" + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } }, "path-is-absolute": { "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -4941,16 +6953,31 @@ }, "path-to-regexp": { "version": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=" + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } }, "path-type": { "version": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=" + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } }, "pbkdf2": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.13.tgz", - "integrity": "sha512-+dCHxDH+djNtjgWmvVC/my3SYBAKpKNqKSjLkp+GtWWYe4XPE+e/PSD2aCanlEZZnqPk2uekTKNC/ccbwd2X2Q==" + "integrity": "sha512-+dCHxDH+djNtjgWmvVC/my3SYBAKpKNqKSjLkp+GtWWYe4XPE+e/PSD2aCanlEZZnqPk2uekTKNC/ccbwd2X2Q==", + "requires": { + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.8" + } }, "performance-now": { "version": "0.2.0", @@ -4968,17 +6995,26 @@ }, "pinkie-promise": { "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + } }, "pkg-dir": { "version": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true + "dev": true, + "requires": { + "find-up": "1.1.2" + } }, "pkg-up": { "version": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", - "dev": true + "dev": true, + "requires": { + "find-up": "1.1.2" + } }, "pluralize": { "version": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", @@ -4989,6 +7025,11 @@ "version": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + }, "dependencies": { "async": { "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -5002,18 +7043,31 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.8.tgz", "integrity": "sha512-G6WnRmdTt2jvJvY+aY+M0AO4YlbxE+slKPZb+jG2P2U9Tyxi3h1fYZ/DgiFU6DC6bv3XIEJoZt+f/kNh8BrWFw==", "dev": true, + "requires": { + "chalk": "2.1.0", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "4.2.1" + }, "dependencies": { "ansi-styles": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true + "dev": true, + "requires": { + "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" + } }, "chalk": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "supports-color": "4.2.1" + } }, "has-flag": { "version": "2.0.0", @@ -5025,7 +7079,10 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz", "integrity": "sha512-qxzYsob3yv6U+xMzPrv170y8AwGP7i74g+pbixCfD6rgso8BscLT2qXIuz6TpOaiJZ3mFgT5O9lyT9nMU4LfaA==", - "dev": true + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } }, @@ -5033,11 +7090,22 @@ "version": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-message-helpers": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "reduce-css-calc": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5045,11 +7113,22 @@ "version": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "dev": true, + "requires": { + "colormin": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5057,11 +7136,21 @@ "version": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5069,11 +7158,20 @@ "version": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5081,11 +7179,20 @@ "version": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5093,11 +7200,20 @@ "version": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5105,11 +7221,20 @@ "version": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5117,11 +7242,21 @@ "version": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "uniqs": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5129,48 +7264,92 @@ "version": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "uniqid": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, "postcss-flexbugs-fixes": { "version": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.0.0.tgz", "integrity": "sha1-ezHLbCfQQXo1pnkUwpX4PEA8ftQ=", - "dev": true + "dev": true, + "requires": { + "postcss": "6.0.8" + } }, "postcss-load-config": { "version": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "postcss-load-options": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "postcss-load-plugins": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz" + } }, "postcss-load-options": { "version": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "postcss-load-plugins": { "version": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "dev": true + "dev": true, + "requires": { + "cosmiconfig": "2.2.2", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "postcss-loader": { "version": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.5.tgz", "integrity": "sha1-wZ0+i4PrGsMW9WIe9MDvWz0bizo=", - "dev": true + "dev": true, + "requires": { + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "postcss": "6.0.8", + "postcss-load-config": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "schema-utils": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz" + } }, "postcss-merge-idents": { "version": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, + "requires": { + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5178,11 +7357,20 @@ "version": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5190,16 +7378,33 @@ "version": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "dev": true, + "requires": { + "browserslist": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "caniuse-api": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-selector-parser": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "vendors": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz" + }, "dependencies": { "browserslist": { "version": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true + "dev": true, + "requires": { + "caniuse-db": "1.0.30000712", + "electron-to-chromium": "1.3.17" + } }, "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5212,11 +7417,22 @@ "version": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, + "requires": { + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5224,11 +7440,21 @@ "version": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5236,11 +7462,23 @@ "version": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, + "requires": { + "alphanum-sort": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "uniqs": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5248,43 +7486,79 @@ "version": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, + "requires": { + "alphanum-sort": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-selector-parser": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, "postcss-modules-extract-imports": { "version": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", - "dev": true + "dev": true, + "requires": { + "postcss": "6.0.8" + } }, "postcss-modules-local-by-default": { "version": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true + "dev": true, + "requires": { + "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "postcss": "6.0.8" + } }, "postcss-modules-scope": { "version": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true + "dev": true, + "requires": { + "css-selector-tokenizer": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "postcss": "6.0.8" + } }, "postcss-modules-values": { "version": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true + "dev": true, + "requires": { + "icss-replace-symbols": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "postcss": "6.0.8" + } }, "postcss-normalize-charset": { "version": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5292,11 +7566,23 @@ "version": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, + "requires": { + "is-absolute-url": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "normalize-url": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5304,11 +7590,21 @@ "version": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5316,11 +7612,21 @@ "version": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5328,11 +7634,20 @@ "version": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, + "requires": { + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5340,28 +7655,56 @@ "version": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, + "requires": { + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, "postcss-selector-parser": { "version": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true + "dev": true, + "requires": { + "flatten": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "indexes-of": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "uniq": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" + } }, "postcss-svgo": { "version": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, + "requires": { + "is-svg": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "postcss-value-parser": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "svgo": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5369,11 +7712,22 @@ "version": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, + "requires": { + "alphanum-sort": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "uniqs": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5386,11 +7740,22 @@ "version": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, + "requires": { + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "postcss": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "uniqs": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" + }, "dependencies": { "postcss": { "version": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "js-base64": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } } } }, @@ -5416,19 +7781,30 @@ "pretty-error": { "version": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true + "dev": true, + "requires": { + "renderkid": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", + "utila": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + } }, "pretty-format": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz", "integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=", "dev": true, + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "ansi-styles": "3.2.0" + }, "dependencies": { "ansi-styles": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true + "dev": true, + "requires": { + "color-convert": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz" + } } } }, @@ -5452,17 +7828,28 @@ "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } }, "prop-types": { "version": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", - "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=" + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", + "requires": { + "fbjs": "0.8.14", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + } }, "proxy-addr": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", - "dev": true + "dev": true, + "requires": { + "forwarded": "0.1.0", + "ipaddr.js": "1.4.0" + } }, "prr": { "version": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", @@ -5477,7 +7864,14 @@ "public-encrypt": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=" + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "parse-asn1": "5.1.0", + "randombytes": "2.0.5" + } }, "punycode": { "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -5497,7 +7891,11 @@ "query-string": { "version": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true + "dev": true, + "requires": { + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "strict-uri-encode": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" + } }, "querystring": { "version": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -5515,27 +7913,43 @@ "randomatic": { "version": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" + }, "dependencies": { "is-number": { "version": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + }, "dependencies": { "kind-of": { "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } } } }, "kind-of": { "version": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=" + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } } } }, "randombytes": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", - "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==" + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "requires": { + "safe-buffer": "5.1.1" + } }, "range-parser": { "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -5546,6 +7960,12 @@ "version": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", "dev": true, + "requires": { + "deep-extend": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "ini": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + }, "dependencies": { "minimist": { "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -5556,7 +7976,14 @@ }, "react": { "version": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", - "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=" + "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=", + "requires": { + "create-react-class": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "fbjs": "0.8.14", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "react-addons-test-utils": { "version": "15.6.0", @@ -5564,17 +7991,36 @@ "integrity": "sha1-Bi02EX/o0Y87peBuszODsLhepbk=", "dev": true }, - "react-codemirror": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/react-codemirror/-/react-codemirror-1.0.0.tgz", - "integrity": "sha1-kUZ7U7H12A2Rai/QtMetuFqQAbo=", - "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": "3.0.2", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-3.0.2.tgz", "integrity": "sha1-GkImPptqoR3LRdad/l6xs1S9VTE=", "dev": true, + "requires": { + "address": "1.0.2", + "anser": "1.4.1", + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "cross-spawn": "4.0.2", + "detect-port-alt": "1.1.3", + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "filesize": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz", + "gzip-size": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "html-entities": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "inquirer": "3.1.1", + "is-root": "1.0.0", + "opn": "5.1.0", + "recursive-readdir": "2.2.1", + "shell-quote": "1.6.1", + "sockjs-client": "1.1.4", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "text-table": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + }, "dependencies": { "ansi-escapes": { "version": "2.0.0", @@ -5592,19 +8038,41 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true + "dev": true, + "requires": { + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + } }, "inquirer": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz", "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==", - "dev": true + "dev": true, + "requires": { + "ansi-escapes": "2.0.0", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "cli-cursor": "2.1.0", + "cli-width": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", + "external-editor": "2.0.4", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } }, "is-fullwidth-code-point": { "version": "2.0.0", @@ -5622,19 +8090,29 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + } }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true + "dev": true, + "requires": { + "is-promise": "2.1.0" + } }, "rx-lite": { "version": "4.0.8", @@ -5647,90 +8125,47 @@ "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 + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } } } } } }, - "react-docgen": { - "version": "3.0.0-beta6", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-beta6.tgz", - "integrity": "sha1-KCe1LFK4ZEci4OIVxb98FcBFjDQ=", - "dev": true, - "dependencies": { - "ast-types": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.11.tgz", - "integrity": "sha1-NxF3u1kjL/XOqh0J7lytcFsaWqk=", - "dev": true - }, - "babylon": { - "version": "7.0.0-beta.17", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.17.tgz", - "integrity": "sha1-Kq1NZ2T0Cd+zrCFthV3JPXDTeRE=", - "dev": true - }, - "core-js": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", - "dev": true - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "recast": { - "version": "0.12.6", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.6.tgz", - "integrity": "sha1-Sw+4L+sdELO9YtNJQ0Jtmz7TDUw=", - "dev": true - } - } - }, - "react-docgen-displayname-handler": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/react-docgen-displayname-handler/-/react-docgen-displayname-handler-1.0.0.tgz", - "integrity": "sha1-n8Gwcc+q0B4lRrJYOC3sM8WeEHs=", - "dev": true, - "dependencies": { - "ast-types": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.0.tgz", - "integrity": "sha1-yHIch0euTVspuSnpnFMXtOh0ViM=", - "dev": true - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "recast": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.12.tgz", - "integrity": "sha1-p55NP4LV1yqC7hd66qeR55O75dY=", - "dev": true - } - } - }, "react-dom": { "version": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", - "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=" + "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=", + "requires": { + "fbjs": "0.8.14", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "react-error-overlay": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-1.0.9.tgz", "integrity": "sha1-mI5I9vNDr6l6cZxN2uUbj+jM/ug=", "dev": true, + "requires": { + "anser": "1.2.5", + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-runtime": "6.23.0", + "react-dev-utils": "3.0.2", + "settle-promise": "1.0.0", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + }, "dependencies": { "anser": { "version": "1.2.5", @@ -5743,6 +8178,10 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", "dev": true, + "requires": { + "core-js": "2.5.0", + "regenerator-runtime": "0.10.5" + }, "dependencies": { "regenerator-runtime": { "version": "0.10.5", @@ -5755,40 +8194,67 @@ } }, "react-event-listener": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.4.5.tgz", - "integrity": "sha1-4+iVoJcM8U7o+JAROvaBl6vz0LE=" - }, - "react-group": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/react-group/-/react-group-1.0.5.tgz", - "integrity": "sha1-ygfWjLuubZklCCnhwy94JYdpPAc=", - "dev": true - }, - "react-icon-base": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.0.7.tgz", - "integrity": "sha1-C9GHNr1s55ym1pzoOHoH+41M7/4=", - "dev": true, + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.5.1.tgz", + "integrity": "sha1-ujYHbke8N8Wmf/XM1Kn/DxViEEA=", + "requires": { + "babel-runtime": "6.26.0", + "fbjs": "0.8.16", + "prop-types": "15.6.0", + "warning": "3.0.0" + }, "dependencies": { + "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.5.0", + "regenerator-runtime": "0.11.0" + } + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.14" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, "prop-types": { - "version": "15.5.8", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.8.tgz", - "integrity": "sha1-a3suFBCDvjjIWVqlH8VXdccZk5Q=", - "dev": true + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", + "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "requires": { + "fbjs": "0.8.16", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } } } }, - "react-icons": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.5.tgz", - "integrity": "sha1-+UJQHCGkzARWziu+5QMsk/YFHc8=", - "dev": true - }, "react-infinite": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/react-infinite/-/react-infinite-0.12.1.tgz", "integrity": "sha512-sOXsm0OsszFQQ+4Vtqt1UUqLETGOCS0keAdEQuNMmeoIHHz2iIW44cHhPLxyeAsdfJQOYanmBZjhpZFQw7bhKw==", + "requires": { + "lodash.isarray": "3.0.4", + "lodash.isfinite": "3.2.0", + "object-assign": "4.0.1" + }, "dependencies": { "object-assign": { "version": "4.0.1", @@ -5800,40 +8266,133 @@ "react-redux": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.5.tgz", - "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=" + "integrity": "sha1-+OjHsjlCJXblLWt9sGQ5RpvphGo=", + "requires": { + "create-react-class": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "hoist-non-react-statics": "1.2.0", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "4.17.4", + "lodash-es": "4.17.4", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "react-router": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.1.1.tgz", - "integrity": "sha1-1Ejzt8G0Kab7sDOVCZlJxgax/pU=" + "integrity": "sha1-1Ejzt8G0Kab7sDOVCZlJxgax/pU=", + "requires": { + "history": "4.6.3", + "hoist-non-react-statics": "1.2.0", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "path-to-regexp": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "warning": "3.0.0" + } }, "react-router-dom": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.1.1.tgz", - "integrity": "sha1-MCGt4fLBYK+Xz5TiVZTF8pRYMCU=" + "integrity": "sha1-MCGt4fLBYK+Xz5TiVZTF8pRYMCU=", + "requires": { + "history": "4.6.3", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "react-router": "4.1.1" + } }, "react-scripts": { "version": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.7.tgz", "integrity": "sha1-/hQ23aA7tFRlx20JfP6k8y63y7s=", "dev": true, + "requires": { + "autoprefixer": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.0.tgz", + "babel-core": "6.24.1", + "babel-eslint": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "babel-jest": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", + "babel-loader": "7.0.0", + "babel-preset-react-app": "3.0.1", + "babel-runtime": "6.23.0", + "case-sensitive-paths-webpack-plugin": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-1.1.4.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "css-loader": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", + "dotenv": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "eslint": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "eslint-config-react-app": "1.0.5", + "eslint-loader": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.7.1.tgz", + "eslint-plugin-flowtype": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.33.0.tgz", + "eslint-plugin-import": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", + "eslint-plugin-jsx-a11y": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz", + "eslint-plugin-react": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.0.1.tgz", + "extract-text-webpack-plugin": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", + "file-loader": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", + "fs-extra": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "fsevents": "1.0.17", + "html-webpack-plugin": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz", + "jest": "20.0.3", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "postcss-flexbugs-fixes": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-3.0.0.tgz", + "postcss-loader": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.5.tgz", + "promise": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "react-dev-utils": "3.0.2", + "react-error-overlay": "1.0.9", + "style-loader": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", + "sw-precache-webpack-plugin": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.9.1.tgz", + "url-loader": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", + "webpack": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", + "webpack-dev-server": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", + "webpack-manifest-plugin": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz", + "whatwg-fetch": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz" + }, "dependencies": { "babel-core": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz", "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=", - "dev": true + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "babel-helpers": "6.24.1", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-register": "6.24.1", + "babel-runtime": "6.23.0", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "lodash": "4.17.4", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "babel-loader": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.0.0.tgz", "integrity": "sha1-LkOma+4f/0RwUz0EAsikUy+vuvc=", - "dev": true + "dev": true, + "requires": { + "find-cache-dir": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } }, "babel-runtime": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", "dev": true, + "requires": { + "core-js": "2.5.0", + "regenerator-runtime": "0.10.5" + }, "dependencies": { "regenerator-runtime": { "version": "0.10.5", @@ -5855,6 +8414,10 @@ "integrity": "sha1-hTfz8SJyZ4dltP1lKMDx9m+PRVg=", "dev": true, "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.32" + }, "dependencies": { "abbrev": { "version": "1.0.9", @@ -5883,7 +8446,11 @@ "version": "1.1.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.2" + } }, "asn1": { "version": "0.2.3", @@ -5924,22 +8491,35 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } }, "block-stream": { "version": "0.0.9", "bundled": true, - "dev": true + "dev": true, + "requires": { + "inherits": "2.0.3" + } }, "boom": { "version": "2.10.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "hoek": "2.16.3" + } }, "brace-expansion": { "version": "1.1.6", "bundled": true, - "dev": true + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } }, "buffer-shims": { "version": "1.0.0", @@ -5957,6 +8537,13 @@ "bundled": true, "dev": true, "optional": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, "dependencies": { "supports-color": { "version": "2.0.0", @@ -5974,13 +8561,19 @@ "combined-stream": { "version": "1.0.5", "bundled": true, - "dev": true + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } }, "commander": { "version": "2.9.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "graceful-readlink": "1.0.1" + } }, "concat-map": { "version": "0.0.1", @@ -6001,13 +8594,19 @@ "version": "2.0.5", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "boom": "2.10.1" + } }, "dashdash": { "version": "1.14.1", "bundled": true, "dev": true, "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -6021,7 +8620,10 @@ "version": "2.2.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "ms": "0.7.1" + } }, "deep-extend": { "version": "0.4.1", @@ -6044,7 +8646,10 @@ "version": "0.1.1", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.0" + } }, "escape-string-regexp": { "version": "1.0.5", @@ -6073,7 +8678,12 @@ "version": "2.1.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.13" + } }, "fs.realpath": { "version": "1.0.0", @@ -6083,19 +8693,41 @@ "fstream": { "version": "1.0.10", "bundled": true, - "dev": true + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.5.4" + } }, "fstream-ignore": { "version": "1.0.5", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "fstream": "1.0.10", + "inherits": "2.0.3", + "minimatch": "3.0.3" + } }, "gauge": { "version": "2.7.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "aproba": "1.0.4", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.0", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "supports-color": "0.2.0", + "wide-align": "1.1.0" + } }, "generate-function": { "version": "2.0.0", @@ -6107,13 +8739,19 @@ "version": "1.2.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "is-property": "1.0.2" + } }, "getpass": { "version": "0.1.6", "bundled": true, "dev": true, "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -6126,7 +8764,15 @@ "glob": { "version": "7.1.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.3", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "graceful-fs": { "version": "4.1.11", @@ -6143,13 +8789,22 @@ "version": "2.0.6", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.9.0", + "is-my-json-valid": "2.15.0", + "pinkie-promise": "2.0.1" + } }, "has-ansi": { "version": "2.0.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "ansi-regex": "2.0.0" + } }, "has-unicode": { "version": "2.0.1", @@ -6161,7 +8816,13 @@ "version": "3.1.3", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } }, "hoek": { "version": "2.16.3", @@ -6172,12 +8833,21 @@ "version": "1.1.1", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.3.1", + "sshpk": "1.10.1" + } }, "inflight": { "version": "1.0.6", "bundled": true, - "dev": true + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } }, "inherits": { "version": "2.0.3", @@ -6193,13 +8863,22 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } }, "is-my-json-valid": { "version": "2.15.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } }, "is-property": { "version": "1.0.2", @@ -6228,7 +8907,10 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "jsbn": "0.1.0" + } }, "jsbn": { "version": "0.1.0", @@ -6258,7 +8940,12 @@ "version": "1.3.1", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + } }, "mime-db": { "version": "1.25.0", @@ -6268,12 +8955,18 @@ "mime-types": { "version": "2.1.13", "bundled": true, - "dev": true + "dev": true, + "requires": { + "mime-db": "1.25.0" + } }, "minimatch": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "requires": { + "brace-expansion": "1.1.6" + } }, "minimist": { "version": "0.0.8", @@ -6283,7 +8976,10 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "minimist": "0.0.8" + } }, "ms": { "version": "0.7.1", @@ -6295,19 +8991,39 @@ "version": "0.6.32", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.0.2", + "rc": "1.1.6", + "request": "2.79.0", + "rimraf": "2.5.4", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.3.0" + } }, "nopt": { "version": "3.0.6", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "abbrev": "1.0.9" + } }, "npmlog": { "version": "4.0.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "are-we-there-yet": "1.1.2", + "console-control-strings": "1.1.0", + "gauge": "2.7.2", + "set-blocking": "2.0.0" + } }, "number-is-nan": { "version": "1.0.1", @@ -6329,7 +9045,10 @@ "once": { "version": "1.4.0", "bundled": true, - "dev": true + "dev": true, + "requires": { + "wrappy": "1.0.2" + } }, "path-is-absolute": { "version": "1.0.1", @@ -6346,7 +9065,10 @@ "version": "2.0.1", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "pinkie": "2.0.4" + } }, "process-nextick-args": { "version": "1.0.7", @@ -6370,6 +9092,12 @@ "bundled": true, "dev": true, "optional": true, + "requires": { + "deep-extend": "0.4.1", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "1.0.4" + }, "dependencies": { "minimist": { "version": "1.2.0", @@ -6383,18 +9111,52 @@ "version": "2.2.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } }, "request": { "version": "2.79.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.5.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "2.1.2", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.13", + "oauth-sign": "0.8.2", + "qs": "6.3.0", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.0.1" + } }, "rimraf": { "version": "2.5.4", "bundled": true, - "dev": true + "dev": true, + "requires": { + "glob": "7.1.1" + } }, "semver": { "version": "5.3.0", @@ -6418,13 +9180,27 @@ "version": "1.0.9", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "hoek": "2.16.3" + } }, "sshpk": { "version": "1.10.1", "bundled": true, "dev": true, "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.0", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.6", + "jodid25519": "1.0.2", + "jsbn": "0.1.0", + "tweetnacl": "0.14.5" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -6434,14 +9210,19 @@ } } }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "dev": true - }, "string-width": { "version": "1.0.2", "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, "dev": true }, "stringstream": { @@ -6453,7 +9234,10 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "ansi-regex": "2.0.0" + } }, "strip-json-comments": { "version": "1.0.4", @@ -6470,25 +9254,52 @@ "tar": { "version": "2.2.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.10", + "inherits": "2.0.3" + } }, "tar-pack": { "version": "3.3.0", "bundled": true, "dev": true, "optional": true, + "requires": { + "debug": "2.2.0", + "fstream": "1.0.10", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.1.5", + "rimraf": "2.5.4", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, "dependencies": { "once": { "version": "1.3.3", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "wrappy": "1.0.2" + } }, "readable-stream": { "version": "2.1.5", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } } } }, @@ -6496,7 +9307,10 @@ "version": "2.3.2", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "punycode": "1.4.1" + } }, "tunnel-agent": { "version": "0.4.3", @@ -6531,13 +9345,19 @@ "version": "1.3.6", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } }, "wide-align": { "version": "1.1.0", "bundled": true, "dev": true, - "optional": true + "optional": true, + "requires": { + "string-width": "1.0.2" + } }, "wrappy": { "version": "1.0.2", @@ -6557,19 +9377,57 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.3.tgz", "integrity": "sha1-5P0FTE8RcKEWoAdh2kz9tz8c3DM=", "dev": true, + "requires": { + "jest-cli": "20.0.4" + }, "dependencies": { "jest-cli": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", - "dev": true + "dev": true, + "requires": { + "ansi-escapes": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "callsites": "2.0.0", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "is-ci": "1.0.10", + "istanbul-api": "1.1.11", + "istanbul-lib-coverage": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "istanbul-lib-instrument": "1.7.4", + "istanbul-lib-source-maps": "1.2.1", + "jest-changed-files": "20.0.3", + "jest-config": "20.0.4", + "jest-docblock": "20.0.3", + "jest-environment-jsdom": "20.0.3", + "jest-haste-map": "20.0.4", + "jest-jasmine2": "20.0.4", + "jest-message-util": "20.0.3", + "jest-regex-util": "20.0.3", + "jest-resolve-dependencies": "20.0.3", + "jest-runtime": "20.0.4", + "jest-snapshot": "20.0.3", + "jest-util": "20.0.3", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "node-notifier": "5.1.2", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "string-length": "1.0.1", + "throat": "3.2.0", + "which": "1.3.0", + "worker-farm": "1.4.1", + "yargs": "7.1.0" + } } } }, "promise": { "version": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", - "dev": true + "dev": true, + "requires": { + "asap": "2.0.6" + } }, "source-list-map": { "version": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", @@ -6580,11 +9438,22 @@ "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" + }, "dependencies": { "yargs": { "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + } } } }, @@ -6592,6 +9461,29 @@ "version": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", "integrity": "sha1-LgRX8KuxrF3zqxBsacZy8jZ4Xwc=", "dev": true, + "requires": { + "acorn": "5.1.1", + "acorn-dynamic-import": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "ajv-keywords": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "async": "2.5.0", + "enhanced-resolve": "3.4.1", + "interpret": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "json-loader": "0.5.7", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "loader-runner": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "loader-utils": "0.2.17", + "memory-fs": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "node-libs-browser": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "tapable": "0.2.8", + "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "watchpack": "1.4.0", + "webpack-sources": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "yargs": "6.6.0" + }, "dependencies": { "camelcase": { "version": "3.0.0", @@ -6603,31 +9495,64 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true + "dev": true, + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wrap-ansi": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" + } }, "loader-utils": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true + "dev": true, + "requires": { + "big.js": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "emojis-list": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "yargs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true + "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "get-caller-file": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "os-locale": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "require-directory": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "require-main-filename": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "which-module": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "y18n": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "yargs-parser": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz" + } } } }, "webpack-sources": { "version": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", - "dev": true + "dev": true, + "requires": { + "source-list-map": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "yargs-parser": { "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + }, "dependencies": { "camelcase": { "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -6638,144 +9563,95 @@ } } }, - "react-styleguidist": { - "version": "5.5.9", - "resolved": "https://registry.npmjs.org/react-styleguidist/-/react-styleguidist-5.5.9.tgz", - "integrity": "sha512-UO2V+OWBzLzvJB0uC+kjZENbGeOvdtTS2nr4HwjzoXcurHyFcMxyI2ACWqhYnLeJqsiqayN4dFXObsp17eMzIQ==", - "dev": true, - "dependencies": { - "ansi-html": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.5.tgz", - "integrity": "sha1-DcqloIEgaGa8JAo7dzoYTqO4i2Q=", - "dev": true - }, - "ast-types": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.12.tgz", - "integrity": "sha1-sTYwDWcCZiWuFTJpgsqZGOXbc8k=", - "dev": true - }, - "css-loader": { - "version": "0.28.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.4.tgz", - "integrity": "sha1-bPNXkZLONV6LONX0Ldeh8uyJjQ8=", - "dev": true - }, - "faye-websocket": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.7.3.tgz", - "integrity": "sha1-zEB0x/Sk39A69U3WXDVLE1EyzhE=", - "dev": true - }, - "html-entities": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.0.tgz", - "integrity": "sha1-QZSMr4XOgv7Tbk5qDtNxpmZDeeI=", - "dev": true - }, - "minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", - "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "dev": true - }, - "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", - "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true - }, - "react-dev-utils": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-0.5.2.tgz", - "integrity": "sha1-UNC5YtOpS2wujyAR7WRo5BJLxBA=", - "dev": true - }, - "recursive-readdir": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.1.1.tgz", - "integrity": "sha1-oBz8f384pT7AlqCW9jpQSJw+KXw=", - "dev": true - }, - "sockjs-client": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.0.1.tgz", - "integrity": "sha1-iUOuBbRlR7wgVIFsQJACz14v4CY=", - "dev": true - }, - "webpack-dev-server": { - "version": "1.16.5", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-1.16.5.tgz", - "integrity": "sha1-DL1fLSrI1OWTqs1clwLnu9XlmJI=", - "dev": true, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true - }, - "sockjs-client": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", - "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", - "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=" + "integrity": "sha1-MWvrO8ZVbinshppyk+icgmqQdNI=", + "requires": { + "fbjs": "0.8.14" + } }, "react-test-renderer": { "version": "15.6.1", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-15.6.1.tgz", "integrity": "sha1-Am9KW7VVJmH9LMS7zQ1LyKNev34=", - "dev": true + "dev": true, + "requires": { + "fbjs": "0.8.14", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } }, "react-tiny-virtual-list": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/react-tiny-virtual-list/-/react-tiny-virtual-list-2.1.4.tgz", - "integrity": "sha512-9JvkWliho5SGHlv/uz3MuObUkg60SPvw3Ottvi/n4nl7qzBvxtb9oI8kJqI9sfZYYGy7xQLbTP0vGhqtnhA04Q==" + "integrity": "sha512-9JvkWliho5SGHlv/uz3MuObUkg60SPvw3Ottvi/n4nl7qzBvxtb9oI8kJqI9sfZYYGy7xQLbTP0vGhqtnhA04Q==", + "requires": { + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "react-transition-group": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.0.tgz", - "integrity": "sha1-tR/JIbDDg1p+98Vxx5/ILHPpIE8=" + "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.1", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "warning": "3.0.0" + } + }, + "react-virtualized": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.13.0.tgz", + "integrity": "sha512-S30oqIXEjF0MnuKElkwCfjSJVnmqbnOCybnEuqhB1g66t1XdIxustgxnDnHXK5AcPkHkkoYgapAAz0eaih8nyA==", + "requires": { + "babel-runtime": "6.25.0", + "classnames": "2.2.5", + "dom-helpers": "3.2.1", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "prop-types": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz" + } }, "read-all-stream": { "version": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", - "dev": true + "dev": true, + "requires": { + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "readable-stream": "2.3.2" + } }, "read-pkg": { "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=" + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "normalize-package-data": "2.4.0", + "path-type": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + } }, "read-pkg-up": { "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "1.1.2", + "read-pkg": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + } }, "readable-stream": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "1.0.0", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, "dependencies": { "isarray": { "version": "1.0.0", @@ -6786,17 +9662,34 @@ }, "readdirp": { "version": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=" + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "readable-stream": "2.3.2", + "set-immediate-shim": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + } }, "readline2": { "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true + "dev": true, + "requires": { + "code-point-at": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "is-fullwidth-code-point": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "mute-stream": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz" + } }, "recast": { "version": "0.10.43", "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.43.tgz", "integrity": "sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8=", + "requires": { + "ast-types": "0.8.15", + "esprima-fb": "15001.1001.0-dev-harmony-fb", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + }, "dependencies": { "esprima-fb": { "version": "15001.1001.0-dev-harmony-fb", @@ -6808,36 +9701,67 @@ "rechoir": { "version": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true + "dev": true, + "requires": { + "resolve": "1.4.0" + } }, "recompose": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.24.0.tgz", - "integrity": "sha512-7+UVym5Mfks/ukIDfcAiasrY61YGki8uIs4CmLTGU7UV2lm2ObbhOl913WrlsZKu8x8uA/sLJUOI5hxVga0dIA==" + "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.6", + "fbjs": "0.8.14", + "hoist-non-react-statics": "2.3.1", + "symbol-observable": "1.0.4" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", + "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=" + } + } }, "recursive-readdir": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz", "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=", "dev": true, + "requires": { + "minimatch": "3.0.3" + }, "dependencies": { "minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", - "dev": true + "dev": true, + "requires": { + "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + } } } }, "redent": { "version": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true + "dev": true, + "requires": { + "indent-string": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "strip-indent": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" + } }, "reduce-css-calc": { "version": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "math-expression-evaluator": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "reduce-function-call": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz" + }, "dependencies": { "balanced-match": { "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", @@ -6850,6 +9774,9 @@ "version": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "dev": true, + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, "dependencies": { "balanced-match": { "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", @@ -6861,7 +9788,13 @@ "redux": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.1.tgz", - "integrity": "sha512-iEVTlORM5mv6xb3ZAOyrVehVUD+W87jdFAX6SYVgZh3/SQAWFSxTRJOqPWQdvo4VN4lJkNDvqKlBXBabsJTSkA==" + "integrity": "sha512-iEVTlORM5mv6xb3ZAOyrVehVUD+W87jdFAX6SYVgZh3/SQAWFSxTRJOqPWQdvo4VN4lJkNDvqKlBXBabsJTSkA==", + "requires": { + "lodash": "4.17.4", + "lodash-es": "4.17.4", + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "symbol-observable": "1.0.4" + } }, "redux-axios-middleware": { "version": "4.0.0", @@ -6883,7 +9816,12 @@ "redux-persist": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.8.2.tgz", - "integrity": "sha1-eUEgLgzgqfzAJj1mll9SY9NPQ7k=" + "integrity": "sha1-eUEgLgzgqfzAJj1mll9SY9NPQ7k=", + "requires": { + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "lodash": "4.17.4", + "lodash-es": "4.17.4" + } }, "regenerate": { "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", @@ -6893,34 +9831,54 @@ "regenerator-runtime": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", - "dev": true + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" }, "regenerator-transform": { "version": "0.9.11", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz", "integrity": "sha1-On0GdSDLe3F2dp61/4aGkb7+EoM=", - "dev": true + "dev": true, + "requires": { + "babel-runtime": "6.25.0", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz" + } }, "regex-cache": { "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", - "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=" + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", + "requires": { + "is-equal-shallow": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true + "dev": true, + "requires": { + "regenerate": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "regjsgen": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "regjsparser": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz" + } }, "registry-auth-token": { "version": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", - "dev": true + "dev": true, + "requires": { + "rc": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "safe-buffer": "5.1.1" + } }, "registry-url": { "version": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true + "dev": true, + "requires": { + "rc": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz" + } }, "regjsgen": { "version": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", @@ -6931,6 +9889,9 @@ "version": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, + "requires": { + "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + }, "dependencies": { "jsesc": { "version": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", @@ -6945,24 +9906,6 @@ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", "dev": true }, - "remark": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-7.0.1.tgz", - "integrity": "sha1-pd5NrPq/D2CkmCbvJMR5gH+QS/s=", - "dev": true - }, - "remark-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-3.0.1.tgz", - "integrity": "sha1-G5+EGkTY9PvyJGhQJlRZpOs1TIA=", - "dev": true - }, - "remark-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-3.0.1.tgz", - "integrity": "sha1-eSQr6+CnUggbWAlRb6DAbt7Aac8=", - "dev": true - }, "remove-trailing-separator": { "version": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=" @@ -6971,6 +9914,13 @@ "version": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "dev": true, + "requires": { + "css-select": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "dom-converter": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", + "htmlparser2": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "utila": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz" + }, "dependencies": { "utila": { "version": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", @@ -6990,19 +9940,40 @@ "repeating": { "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true + "dev": true, + "requires": { + "is-finite": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz" + } }, "request": { "version": "2.81.0", "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "mime-types": "2.1.16", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } }, "require-directory": { "version": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7021,7 +9992,11 @@ "require-uncached": { "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true + "dev": true, + "requires": { + "caller-path": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "resolve-from": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz" + } }, "requires-port": { "version": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7032,7 +10007,10 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", - "dev": true + "dev": true, + "requires": { + "path-parse": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz" + } }, "resolve-from": { "version": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", @@ -7047,26 +10025,43 @@ "restore-cursor": { "version": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true + "dev": true, + "requires": { + "exit-hook": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "onetime": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" + } }, "right-align": { "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=" + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz" + } }, "rimraf": { "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + } }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=" + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "requires": { + "hash-base": "2.0.2", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } }, "run-async": { "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true + "dev": true, + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + } }, "rx-lite": { "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", @@ -7077,7 +10072,10 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true + "dev": true, + "requires": { + "rx-lite": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz" + } }, "safe-buffer": { "version": "5.1.1", @@ -7089,18 +10087,33 @@ "resolved": "https://registry.npmjs.org/sane/-/sane-1.6.0.tgz", "integrity": "sha1-lhDEUjB6E10pwf3+JUcDQYDEZ3U=", "dev": true, + "requires": { + "anymatch": "1.3.2", + "exec-sh": "0.2.0", + "fb-watchman": "1.9.2", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "minimist": "1.2.0", + "walker": "1.0.7", + "watch": "0.10.0" + }, "dependencies": { "bser": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", "integrity": "sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=", - "dev": true + "dev": true, + "requires": { + "node-int64": "0.4.0" + } }, "fb-watchman": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.2.tgz", "integrity": "sha1-okz0eCf4LTj7Waaa1wt247auc4M=", - "dev": true + "dev": true, + "requires": { + "bser": "1.0.2" + } }, "minimist": { "version": "1.2.0", @@ -7120,12 +10133,21 @@ "version": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, + "requires": { + "ajv": "5.2.2" + }, "dependencies": { "ajv": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", - "dev": true + "dev": true, + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + } }, "fast-deep-equal": { "version": "1.0.0", @@ -7148,19 +10170,31 @@ "semver-diff": { "version": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true - }, - "semver-utils": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.1.tgz", - "integrity": "sha1-J9kv7DTSfPpCcH07QNAlrphV8t8=", - "dev": true + "dev": true, + "requires": { + "semver": "5.4.1" + } }, "send": { "version": "0.15.4", "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "etag": "1.8.0", + "fresh": "0.5.0", + "http-errors": "1.6.2", + "mime": "1.3.4", + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "on-finished": "2.3.0", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "statuses": "1.3.1" + }, "dependencies": { "mime": { "version": "1.3.4", @@ -7173,13 +10207,28 @@ "serve-index": { "version": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", "integrity": "sha1-0rKA/FYNYW7oG0i/D6gqvtJIXOc=", - "dev": true + "dev": true, + "requires": { + "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "batch": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "http-errors": "1.6.2", + "mime-types": "2.1.16", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz" + } }, "serve-static": { "version": "1.12.4", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", - "dev": true + "dev": true, + "requires": { + "encodeurl": "1.0.1", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "send": "0.15.4" + } }, "serviceworker-cache-polyfill": { "version": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", @@ -7214,18 +10263,32 @@ "sha.js": { "version": "2.4.8", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", - "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=" + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } }, "shell-quote": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", - "dev": true + "dev": true, + "requires": { + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } }, "shelljs": { "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "interpret": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "rechoir": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" + } }, "shellwords": { "version": "0.1.0", @@ -7262,17 +10325,27 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true + "dev": true, + "requires": { + "hoek": "2.16.3" + } }, "sockjs": { "version": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", "dev": true, + "requires": { + "faye-websocket": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz" + }, "dependencies": { "faye-websocket": { "version": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true + "dev": true, + "requires": { + "websocket-driver": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz" + } }, "uuid": { "version": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", @@ -7285,12 +10358,23 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "eventsource": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "faye-websocket": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "url-parse": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz" + } }, "sort-keys": { "version": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true + "dev": true, + "requires": { + "is-plain-obj": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" + } }, "source-list-map": { "version": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", @@ -7305,18 +10389,18 @@ "version": "0.4.15", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", - "dev": true - }, - "sparkles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", - "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", - "dev": true + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=" + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "requires": { + "spdx-license-ids": "1.2.2" + } }, "spdx-expression-parse": { "version": "1.0.4", @@ -7331,12 +10415,29 @@ "spdy": { "version": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "handle-thing": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "http-deceiver": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "safe-buffer": "5.1.1", + "select-hose": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "spdy-transport": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz" + } }, "spdy-transport": { "version": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.0.20.tgz", "integrity": "sha1-c15yBUxIayNU/onnAiVgBKOazk0=", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "detect-node": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", + "hpack.js": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "obuf": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", + "readable-stream": "2.3.2", + "safe-buffer": "5.1.1", + "wbuf": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz" + } }, "sprintf-js": { "version": "1.0.3", @@ -7349,6 +10450,16 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, "dependencies": { "assert-plus": { "version": "1.0.0", @@ -7358,12 +10469,6 @@ } } }, - "state-toggle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", - "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", - "dev": true - }, "statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", @@ -7372,43 +10477,53 @@ }, "stream-browserify": { "version": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=" - }, - "stream-cache": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream-cache/-/stream-cache-0.0.2.tgz", - "integrity": "sha1-GsWtaDJCjKVWZ9ve45Xa1ObbEY8=", - "dev": true + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "2.3.2" + } }, "stream-http": { "version": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=" + "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=", + "requires": { + "builtin-status-codes": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "2.3.2", + "to-arraybuffer": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } }, "strict-uri-encode": { "version": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==" - }, "string-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", - "dev": true + "dev": true, + "requires": { + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } }, "string-width": { "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "is-fullwidth-code-point": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } }, - "stringify-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", - "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=", - "dev": true + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } }, "stringstream": { "version": "0.0.5", @@ -7418,16 +10533,25 @@ }, "strip-ansi": { "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } }, "strip-bom": { "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=" + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + } }, "strip-indent": { "version": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true + "dev": true, + "requires": { + "get-stdin": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + } }, "strip-json-comments": { "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -7437,16 +10561,31 @@ "style-loader": { "version": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", "integrity": "sha1-6CVLzNt690vVgnTjYQe01atN8xA=", - "dev": true + "dev": true, + "requires": { + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz" + } }, "supports-color": { "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" + } }, "svgo": { "version": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "dev": true, + "requires": { + "coa": "1.0.4", + "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "csso": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "js-yaml": "3.7.0", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "sax": "1.2.4", + "whet.extend": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz" + }, "dependencies": { "esprima": { "version": "2.7.3", @@ -7458,36 +10597,72 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3" + } } } }, "sw-precache": { "version": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.0.tgz", "integrity": "sha1-62IlzlgM6q4UgZRXigrQGrfqGZw=", - "dev": true + "dev": true, + "requires": { + "dom-urls": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", + "es6-promise": "4.1.0", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "lodash.defaults": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "lodash.template": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "meow": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "pretty-bytes": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "sw-toolbox": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", + "update-notifier": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz" + } }, "sw-precache-webpack-plugin": { "version": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-0.9.1.tgz", "integrity": "sha1-I4H/cG+7bKvbIKIDN96OWPtJoqc=", "dev": true, + "requires": { + "del": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "sw-precache": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.0.tgz", + "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz" + }, "dependencies": { "uglify-js": { "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" + } }, "yargs": { "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + } } } }, "sw-toolbox": { "version": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=", - "dev": true + "dev": true, + "requires": { + "path-to-regexp": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "serviceworker-cache-polyfill": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz" + } }, "symbol-observable": { "version": "1.0.4", @@ -7504,6 +10679,14 @@ "version": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "ajv-keywords": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "lodash": "4.17.4", + "slice-ansi": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "string-width": "2.1.1" + }, "dependencies": { "ansi-regex": { "version": "3.0.0", @@ -7520,13 +10703,20 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true + "dev": true, + "requires": { + "is-fullwidth-code-point": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "strip-ansi": "4.0.0" + } }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } } } }, @@ -7538,7 +10728,14 @@ "test-exclude": { "version": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", "integrity": "sha1-TYSWSwlmsAh+zDNKLOAC09k0HiY=", - "dev": true + "dev": true, + "requires": { + "arrify": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "require-main-filename": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" + } }, "text-table": { "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7559,11 +10756,21 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": "1.0.34", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, "dependencies": { "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=" + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } }, "string_decoder": { "version": "0.10.31", @@ -7580,13 +10787,20 @@ "timers-browserify": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.3.tgz", - "integrity": "sha512-+JAqyNgg+M8+gXIrq2EeUr4kZqRz47Ysco7X5QKRGScRE9HIHckyHD1asozSFGeqx2nmPCgA8T5tIGVO0ML7/w==" + "integrity": "sha512-+JAqyNgg+M8+gXIrq2EeUr4kZqRz47Ysco7X5QKRGScRE9HIHckyHD1asozSFGeqx2nmPCgA8T5tIGVO0ML7/w==", + "requires": { + "global": "4.3.2", + "setimmediate": "1.0.5" + } }, "tmp": { "version": "0.0.31", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true + "dev": true, + "requires": { + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } }, "tmpl": { "version": "1.0.4", @@ -7598,26 +10812,6 @@ "version": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" }, - "to-ast": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-ast/-/to-ast-1.0.0.tgz", - "integrity": "sha1-DEoxyMmO396arwGSx5S0yLEe4oc=", - "dev": true, - "dependencies": { - "ast-types": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", - "integrity": "sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk=", - "dev": true - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - } - } - }, "to-fast-properties": { "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", @@ -7637,7 +10831,10 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true + "dev": true, + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } }, "tr46": { "version": "0.0.3", @@ -7645,12 +10842,6 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, "trim-newlines": { "version": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", @@ -7661,18 +10852,6 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, - "trim-trailing-lines": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", - "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=", - "dev": true - }, - "trough": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.1.tgz", - "integrity": "sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=", - "dev": true - }, "tryit": { "version": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", @@ -7686,7 +10865,10 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } }, "tweetnacl": { "version": "0.14.5", @@ -7698,19 +10880,20 @@ "type-check": { "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true - }, - "type-detect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", - "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", - "dev": true + "dev": true, + "requires": { + "prelude-ls": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + } }, "type-is": { "version": "1.6.15", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "dev": true + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.16" + } }, "typedarray": { "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -7726,7 +10909,11 @@ "version": "3.0.27", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.27.tgz", "integrity": "sha512-HD8CmxPXUI62v5tweiulMcP/apAtx1DXGcNZkhKQZyC+MTrTsoCBb8yPAwVrbvpgw3EpRU76bRe6axjIiCYcQg==", - "dev": true + "dev": true, + "requires": { + "commander": "2.11.0", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "uglify-to-browserify": { "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", @@ -7737,6 +10924,11 @@ "version": "0.4.6", "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-js": "2.8.29", + "webpack-sources": "1.0.1" + }, "dependencies": { "source-list-map": { "version": "2.0.0", @@ -7746,32 +10938,35 @@ "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=" + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "3.10.0" + } }, "webpack-sources": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", - "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==" + "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==", + "requires": { + "source-list-map": "2.0.0", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=" + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + } } } }, - "unherit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", - "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=", - "dev": true - }, - "unified": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.5.tgz", - "integrity": "sha1-cWk3hyYhpjE15iztLzrGoGPG+4c=", - "dev": true - }, "uniq": { "version": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", @@ -7780,37 +10975,16 @@ "uniqid": { "version": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true + "dev": true, + "requires": { + "macaddress": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz" + } }, "uniqs": { "version": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", "dev": true }, - "unist-util-modify-children": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz", - "integrity": "sha1-ZtfmpEnm9nIguXarPLi166w55R0=", - "dev": true - }, - "unist-util-remove-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", - "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=", - "dev": true - }, - "unist-util-stringify-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz", - "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw=", - "dev": true - }, - "unist-util-visit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.1.3.tgz", - "integrity": "sha1-7CaOcxudJ3p5pbWqBkOZDkBdYAs=", - "dev": true - }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", @@ -7826,7 +11000,12 @@ "unreachable-branch-transform": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz", - "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=" + "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=", + "requires": { + "esmangle-evaluator": "1.0.1", + "recast": "0.10.43", + "through2": "0.6.5" + } }, "unzip-response": { "version": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", @@ -7836,7 +11015,17 @@ "update-notifier": { "version": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=", - "dev": true + "dev": true, + "requires": { + "boxen": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "configstore": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "is-npm": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "latest-version": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", + "lazy-req": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", + "semver-diff": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "xdg-basedir": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz" + } }, "upper-case": { "version": "1.1.3", @@ -7852,6 +11041,10 @@ "url": { "version": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "querystring": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, "dependencies": { "punycode": { "version": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -7862,12 +11055,20 @@ "url-loader": { "version": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", "integrity": "sha1-uRg7GAHg+EdxhnNnMEC8ncHHFcU=", - "dev": true + "dev": true, + "requires": { + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "mime": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz" + } }, "url-parse": { "version": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", "dev": true, + "requires": { + "querystringify": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + }, "dependencies": { "querystringify": { "version": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", @@ -7879,16 +11080,25 @@ "url-parse-lax": { "version": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true + "dev": true, + "requires": { + "prepend-http": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" + } }, "user-home": { "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true + "dev": true, + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + } }, "util": { "version": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, "dependencies": { "inherits": { "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", @@ -7920,7 +11130,11 @@ "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=" + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } }, "value-equal": { "version": "0.2.1", @@ -7942,40 +11156,34 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true - }, - "vfile": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.2.0.tgz", - "integrity": "sha1-zkek+zNZIrIz5TXbD32BIdj87U4=", - "dev": true - }, - "vfile-location": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz", - "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU=", - "dev": true - }, - "vlq": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", - "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=", - "dev": true + "dev": true, + "requires": { + "extsprintf": "1.0.2" + } }, "vm-browserify": { "version": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=" + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" + } }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true + "dev": true, + "requires": { + "makeerror": "1.0.11" + } }, "warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=" + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + } }, "watch": { "version": "0.10.0", @@ -7986,12 +11194,20 @@ "watchpack": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=" + "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", + "requires": { + "async": "2.5.0", + "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } }, "wbuf": { "version": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.2.tgz", "integrity": "sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=", - "dev": true + "dev": true, + "requires": { + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } }, "webidl-conversions": { "version": "4.0.1", @@ -8003,11 +11219,41 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.0.0.tgz", "integrity": "sha1-7pvOvyEkf3FTy0EBaMq0XjpZ1Nc=", + "requires": { + "acorn": "5.1.1", + "acorn-dynamic-import": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "ajv": "5.2.0", + "ajv-keywords": "2.1.0", + "async": "2.5.0", + "enhanced-resolve": "3.4.1", + "escope": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "interpret": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "json-loader": "0.5.7", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "loader-runner": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "loader-utils": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "memory-fs": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "node-libs-browser": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "tapable": "0.2.8", + "uglifyjs-webpack-plugin": "0.4.6", + "watchpack": "1.4.0", + "webpack-sources": "1.0.1", + "yargs": "6.6.0" + }, "dependencies": { "ajv": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.0.tgz", - "integrity": "sha1-wXNQJMXaLvdcwZBxMHPUTwmL9IY=" + "integrity": "sha1-wXNQJMXaLvdcwZBxMHPUTwmL9IY=", + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "fast-deep-equal": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + } }, "ajv-keywords": { "version": "2.1.0", @@ -8022,7 +11268,12 @@ "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=" + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wrap-ansi": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" + } }, "source-list-map": { "version": "2.0.0", @@ -8032,17 +11283,39 @@ "webpack-sources": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", - "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==" + "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==", + "requires": { + "source-list-map": "2.0.0", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "yargs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=" + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "get-caller-file": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "os-locale": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "require-directory": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "require-main-filename": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "which-module": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "y18n": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "yargs-parser": "4.2.1" + } }, "yargs-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=" + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "requires": { + "camelcase": "3.0.0" + } } } }, @@ -8050,12 +11323,37 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz", "integrity": "sha1-CWkdCXOjCtH4Ksc6EuIIfwpHVPk=", - "dev": true + "dev": true, + "requires": { + "memory-fs": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "mime": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + } }, "webpack-dev-server": { "version": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz", "integrity": "sha1-MThM6BE2vhCAtLTN4OubkOVO5s8=", "dev": true, + "requires": { + "ansi-html": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "compression": "1.7.0", + "connect-history-api-fallback": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz", + "express": "4.15.4", + "html-entities": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "http-proxy-middleware": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "opn": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "portfinder": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "serve-index": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", + "sockjs": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", + "sockjs-client": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", + "spdy": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "webpack-dev-middleware": "1.11.0", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz" + }, "dependencies": { "camelcase": { "version": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -8065,27 +11363,62 @@ "cliui": { "version": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true + "dev": true, + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wrap-ansi": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" + } }, "opn": { "version": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "dev": true + "dev": true, + "requires": { + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } }, "sockjs-client": { "version": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.2.tgz", "integrity": "sha1-8CEqhVDkyUaMjM6u79LjSTwDOtU=", - "dev": true + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "eventsource": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "faye-websocket": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "url-parse": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz" + } }, "yargs": { "version": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "get-caller-file": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "os-locale": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "require-directory": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "require-main-filename": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "which-module": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "y18n": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "yargs-parser": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz" + } }, "yargs-parser": { "version": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + } } } }, @@ -8093,40 +11426,49 @@ "version": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz", "integrity": "sha1-a2xxiq3oolN5lXhLRr0umDYFfKo=", "dev": true, + "requires": { + "fs-extra": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "lodash": "4.17.4" + }, "dependencies": { "fs-extra": { "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jsonfile": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "klaw": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz" + } }, "jsonfile": { "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } } } }, - "webpack-merge": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.0.tgz", - "integrity": "sha1-atciI7PguDflMeRZfBmfkJNhUR4=", - "dev": true - }, "webpack-sources": { "version": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", - "dev": true - }, - "webpage": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/webpage/-/webpage-0.3.0.tgz", - "integrity": "sha1-FcjJnoIrSZ6Zga5odlObQjRIuOc=", - "dev": true + "dev": true, + "requires": { + "source-list-map": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } }, "websocket-driver": { "version": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", - "dev": true + "dev": true, + "requires": { + "websocket-extensions": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz" + } }, "websocket-extensions": { "version": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", @@ -8138,6 +11480,9 @@ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz", "integrity": "sha1-PGxFGhmO567FWx7GHQkgxngBpfQ=", "dev": true, + "requires": { + "iconv-lite": "0.4.13" + }, "dependencies": { "iconv-lite": { "version": "0.4.13", @@ -8156,6 +11501,10 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", "integrity": "sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=", "dev": true, + "requires": { + "tr46": "0.0.3", + "webidl-conversions": "3.0.1" + }, "dependencies": { "webidl-conversions": { "version": "3.0.1", @@ -8174,7 +11523,10 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true + "dev": true, + "requires": { + "isexe": "2.0.0" + } }, "which-module": { "version": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", @@ -8183,7 +11535,10 @@ "widest-line": { "version": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", - "dev": true + "dev": true, + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + } }, "window-size": { "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -8198,11 +11553,19 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.4.1.tgz", "integrity": "sha512-tgFAtgOYLPutkAyzgpS6VJFL5HY+0ui1Tvua+fITgz8ByaJTMFGtazR6xxQfwfiAcbwE+2fLG/K49wc2TfwCNw==", - "dev": true + "dev": true, + "requires": { + "errno": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } }, "wrap-ansi": { "version": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=" + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } }, "wrappy": { "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8212,29 +11575,28 @@ "write": { "version": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true + "dev": true, + "requires": { + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } }, "write-file-atomic": { "version": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", - "dev": true - }, - "x-is-function": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", - "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=", - "dev": true - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", - "dev": true + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "imurmurhash": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "slide": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" + } }, "xdg-basedir": { "version": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", - "dev": true + "dev": true, + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + } }, "xml": { "version": "1.0.1", @@ -8273,6 +11635,21 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "get-caller-file": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "os-locale": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "require-directory": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "require-main-filename": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "which-module": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "y18n": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "yargs-parser": "5.0.0" + }, "dependencies": { "camelcase": { "version": "3.0.0", @@ -8284,7 +11661,12 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true + "dev": true, + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wrap-ansi": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" + } } } }, @@ -8293,6 +11675,9 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "dev": true, + "requires": { + "camelcase": "3.0.0" + }, "dependencies": { "camelcase": { "version": "3.0.0", diff --git a/viscoll-app/package.json b/viscoll-app/package.json index a86618b8..e59b22dc 100644 --- a/viscoll-app/package.json +++ b/viscoll-app/package.json @@ -12,16 +12,20 @@ "immutability-helper": "^2.4.0", "js-file-download": "^0.4.1", "localforage": "^1.5.0", - "material-ui": "^0.19.0", + "lodash": "^4.17.4", + "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.13.0", "redux": "^3.7.1", "redux-axios-middleware": "^4.0.0", "redux-persist": "^4.8.2", @@ -39,8 +43,8 @@ "jest-junit-reporter": "^1.1.0", "react-addons-test-utils": "^15.6.0", "react-scripts": "1.0.7", - "react-styleguidist": "^5.5.9", "react-test-renderer": "^15.6.1", + "redux-devtools-extension": "^2.13.2", "redux-mock-store": "^1.2.3", "regenerator-runtime": "^0.11.0" }, @@ -48,9 +52,7 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "jest", - "eject": "react-scripts eject", - "styleguide": "styleguidist server", - "styleguide:build": "styleguidist build" + "eject": "react-scripts eject" }, "babel": { "presets": [ diff --git a/viscoll-app/sass/components/_dialog.scss b/viscoll-app/sass/components/_dialog.scss index 36a7b171..5a588dff 100644 --- a/viscoll-app/sass/components/_dialog.scss +++ b/viscoll-app/sass/components/_dialog.scss @@ -49,11 +49,12 @@ font-weight: normal; text-transform: inherit; } - h2 { + h3 { font-size: 1em; + margin-top: 0em; } .section { - margin-left: 2em; + margin-left: 1em; } .newProjectSelection { button.btnSelection { diff --git a/viscoll-app/sass/index.scss b/viscoll-app/sass/index.scss index 1cb8539c..8a5a91ea 100644 --- a/viscoll-app/sass/index.scss +++ b/viscoll-app/sass/index.scss @@ -13,6 +13,7 @@ @import 'layout/404'; @import 'layout/dashboard'; @import 'layout/imageManager'; +@import 'layout/imageCollection'; @import 'components/dialog'; @import 'components/tooltip'; @import 'components/textarea'; 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 index 2d634f50..09528a89 100644 --- a/viscoll-app/sass/layout/_imageManager.scss +++ b/viscoll-app/sass/layout/_imageManager.scss @@ -12,16 +12,21 @@ } } .manageManifests { - padding: 0em 2em; + 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; - font-size: 1.1em; - font-weight: 500; + span { - font-size: 0.8em; + font-size: 1em; font-weight: normal; color: transparentize($black, 0.2); } @@ -32,9 +37,27 @@ 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 { - .draggableItem { + .moveableItem { height: 50px; background: $white; border-width: 0px 1px 0px 1px; @@ -50,9 +73,11 @@ .text { color: $black; padding-left: 0.5em; + @media screen and (max-width: $xsmall) { + font-size: 0.9em; + } &>span { font-size: 0.8em; - color: transparentize($black, 0.3); } } .thumbnail { @@ -72,6 +97,9 @@ background: $white; margin: 0.5em 1em; padding: 0.2em 0em; + @media screen and (max-width: $small) { + margin: 0.5em 0em; + } } .panelBar { @@ -159,7 +187,7 @@ justify-content: space-evenly; border-bottom: 2px solid $gray; background: $white; - + width: 100%; .title { width:70px; padding-top: 10px; diff --git a/viscoll-app/sass/layout/_infobox.scss b/viscoll-app/sass/layout/_infobox.scss index 52574517..51030c98 100644 --- a/viscoll-app/sass/layout/_infobox.scss +++ b/viscoll-app/sass/layout/_infobox.scss @@ -13,6 +13,12 @@ .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; @@ -25,6 +31,9 @@ } .input { width: 55%; + @media screen and (max-width: $xsmall) { + width: 50%; + } } } button.image { diff --git a/viscoll-app/sass/layout/_notes.scss b/viscoll-app/sass/layout/_notes.scss index 4c563e37..7acee51b 100644 --- a/viscoll-app/sass/layout/_notes.scss +++ b/viscoll-app/sass/layout/_notes.scss @@ -16,8 +16,7 @@ } .details { position: relative; - left: 256px; - width: 65%; + width: 63%; } } .noteType { @@ -73,9 +72,16 @@ .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; diff --git a/viscoll-app/sass/layout/_sidebar.scss b/viscoll-app/sass/layout/_sidebar.scss index 26d38650..d475fb9c 100644 --- a/viscoll-app/sass/layout/_sidebar.scss +++ b/viscoll-app/sass/layout/_sidebar.scss @@ -3,11 +3,16 @@ display: block; top: 55px; width: 18%; - height: 99%; + 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; @@ -17,6 +22,7 @@ margin: 0; } h1 { + text-transform: uppercase; color: $white; font-size: 1em; font-weight: 600; @@ -31,12 +37,18 @@ &: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; @@ -45,6 +57,9 @@ display: none; } } + &:last-child { + margin-bottom: 50px; + } } .selectMode { padding: 1em 1em 2em 1em; @@ -68,9 +83,9 @@ } background: darken($bg_blue, 3%); } - .manager { + + .navigation { text-align: center; - padding: 0.5em 0em; margin: 0px; text-transform: uppercase; @include transition(all, 100ms, ease-in-out); @@ -79,22 +94,43 @@ 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 { diff --git a/viscoll-app/sass/layout/_tabular.scss b/viscoll-app/sass/layout/_tabular.scss index d66ed4b7..ee25668e 100644 --- a/viscoll-app/sass/layout/_tabular.scss +++ b/viscoll-app/sass/layout/_tabular.scss @@ -74,6 +74,9 @@ font-weight: 500; padding-left: 20px; min-height: 45px; + @media screen and (max-width:$xsmall) { + font-size: 0.9em; + } } .itemAttributes { flex-grow: 4; @@ -90,12 +93,18 @@ 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) { diff --git a/viscoll-app/sass/layout/_topbar.scss b/viscoll-app/sass/layout/_topbar.scss index deb9b2b9..74424f3c 100644 --- a/viscoll-app/sass/layout/_topbar.scss +++ b/viscoll-app/sass/layout/_topbar.scss @@ -2,9 +2,13 @@ position: fixed; left:0px; width: 100%; - z-index: 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%; @@ -20,6 +24,9 @@ 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 index bd86834e..66a111ac 100644 --- a/viscoll-app/sass/layout/_workspace.scss +++ b/viscoll-app/sass/layout/_workspace.scss @@ -11,7 +11,6 @@ display: flex; &>div:first-child { margin-top: 4px; - margin-left: 15px; } &>div:nth-child(2) { margin-top: 14px; diff --git a/viscoll-app/sass/lib/_variables.scss b/viscoll-app/sass/lib/_variables.scss index a0598287..e45ef30e 100644 --- a/viscoll-app/sass/lib/_variables.scss +++ b/viscoll-app/sass/lib/_variables.scss @@ -12,6 +12,6 @@ $success: #34A251; $warning: #E37A05; // Breakpoints -$medium: max-width 1200px; -$small: max-width 992px; -$xsmall: max-width 768px; +$medium: 1200px; +$small: 1024px; +$xsmall: 768px; diff --git a/viscoll-app/sass/typography.scss b/viscoll-app/sass/typography.scss index fdd67601..f395be97 100644 --- a/viscoll-app/sass/typography.scss +++ b/viscoll-app/sass/typography.scss @@ -1,11 +1,21 @@ h1 { - font-size: 1.5em; - text-transform: uppercase; + font-size: 1.6em; font-weight: bold; color: $black; + @media screen and (max-width: $xsmall) { + font-size: 1.3em; + } } h2 { - font-weight: normal; - color: $black; + 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..a2b6d04d --- /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 = Object.keys(allObjects[type]); + } else if (select==="matching"){ + selectedObjects.members = Object.keys(allObjects[type]).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..b149863e --- /dev/null +++ b/viscoll-app/src/actions/backend/groupActions.js @@ -0,0 +1,74 @@ + +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" + } + } + }; +} + +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" + } + } + }; +} + +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" + } + } + }; +} + +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" + } + } + }; +} + +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" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/backend/imageActions.js b/viscoll-app/src/actions/backend/imageActions.js new file mode 100644 index 00000000..1e943243 --- /dev/null +++ b/viscoll-app/src/actions/backend/imageActions.js @@ -0,0 +1,95 @@ + +/** + * + * @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" + } + } + }; +} + +/** + * + * @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" + } + } + }; +} + +/** + * + * @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" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/editCollation/interactionActions.js b/viscoll-app/src/actions/backend/interactionActions.js similarity index 57% rename from viscoll-app/src/actions/editCollation/interactionActions.js rename to viscoll-app/src/actions/backend/interactionActions.js index d630f91e..4db55b18 100644 --- a/viscoll-app/src/actions/editCollation/interactionActions.js +++ b/viscoll-app/src/actions/backend/interactionActions.js @@ -81,12 +81,12 @@ export function handleObjectClick(selectedObjects, object, event, objects) { selectedObjects.members = [object.id]; } else { // Select all similar type objects within this object and last selected object - const orderOfCurrentElement = Object.keys(objects[object.memberType+"s"]).indexOf(object.id) - const orderOfLastElement = Object.keys(objects[object.memberType+"s"]).indexOf(selectedObjects.lastSelected) + 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 = Object.keys(objects[object.memberType+"s"]).slice(indexes[0], indexes[1]+1); + 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); @@ -94,6 +94,8 @@ export function handleObjectClick(selectedObjects, object, event, objects) { } } } + // 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 = ""; @@ -166,147 +168,6 @@ export function changeInfoBoxTab(newType, selectedObjects, objects) { }; } - - -export function flashLeaves(request) { - let leavesToFlash = []; - for (let i = request.order; i < (request.order+(request.noOfLeafs)); i++) { - leavesToFlash.push(i); - } - return { - type: 'FLASH_LEAVES', - payload: leavesToFlash - }; -} - -export function flashGroups(request) { - let groupsToFlash = []; - for (let i = request.order; i < (request.order+request.noOfGroups); i++) { - groupsToFlash.push(i); - } - return { - type: 'FLASH_GROUPS', - payload: groupsToFlash - }; -} - - -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(currentQueries, queryIndex, fieldName, index, value) { - let newQueries = [...currentQueries]; - if (fieldName==="attribute") { - newQueries[queryIndex]["attributeIndex"] = index; - } - newQueries[queryIndex][fieldName] = value; - 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 = Object.keys(allObjects[type]); - } else if (select==="matching"){ - selectedObjects.members = Object.keys(allObjects[type]).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 - } - }; - -} - export function toggleVisualizationDrawing(request) { return { type: 'TOGGLE_VISUALIZATION_DRAWING', diff --git a/viscoll-app/src/actions/backend/leafActions.js b/viscoll-app/src/actions/backend/leafActions.js new file mode 100644 index 00000000..ffb9355a --- /dev/null +++ b/viscoll-app/src/actions/backend/leafActions.js @@ -0,0 +1,115 @@ + +export function addLeafs(leaf, additional) { + return { + types: ['CREATE_LEAVES_FRONTEND','CREATE_LEAVES_SUCCESS_BACKEND','CREATE_LEAVES_FAILED_BACKEND'], + payload: { + request : { + url: `/leafs`, + method: 'post', + data: { leaf, additional }, + successMessage: "Successfully added the leaf(s)" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +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" + } + } + }; +} + +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 leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + +/** + 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 leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + + + +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" + } + } + }; +} + +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 leafs" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} + +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: `/leafs/generateFolio`, + method: 'put', + data: {startNumber, rectoIDs, versoIDs}, + successMessage: "Successfully generated the folio numbers" , + errorMessage: "Ooops! Something went wrong" + } + } + }; +} \ 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..cb404e52 --- /dev/null +++ b/viscoll-app/src/actions/backend/manifestActions.js @@ -0,0 +1,68 @@ + + + +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" + } + } + }; +} + + +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" + } + } + }; +} + +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..9f5805f4 --- /dev/null +++ b/viscoll-app/src/actions/backend/noteActions.js @@ -0,0 +1,177 @@ + +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" + } + } + }; +} + + +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" + } + } + }; +} + +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" + } + } + }; +} + + +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" + } + } + }; +} + + +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" + } + } + }; +} + + +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" + } + } + }; +} \ 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..6a999db3 --- /dev/null +++ b/viscoll-app/src/actions/backend/projectActions.js @@ -0,0 +1,139 @@ +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 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 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..5834260b --- /dev/null +++ b/viscoll-app/src/actions/backend/sideActions.js @@ -0,0 +1,29 @@ +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" + } + } + }; +} + +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" + } + } + }; +} \ No newline at end of file diff --git a/viscoll-app/src/actions/userActions.js b/viscoll-app/src/actions/backend/userActions.js similarity index 99% rename from viscoll-app/src/actions/userActions.js rename to viscoll-app/src/actions/backend/userActions.js index 203340c2..10edcd03 100644 --- a/viscoll-app/src/actions/userActions.js +++ b/viscoll-app/src/actions/backend/userActions.js @@ -150,3 +150,4 @@ export function sendFeedback(title, message, browserInformation, project) { } } } + diff --git a/viscoll-app/src/actions/editCollation/modificationActions.js b/viscoll-app/src/actions/editCollation/modificationActions.js deleted file mode 100644 index 38b47942..00000000 --- a/viscoll-app/src/actions/editCollation/modificationActions.js +++ /dev/null @@ -1,411 +0,0 @@ -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 addLeafs(leaf, additional) { - return { - types: ['SHOW_LOADING','ADD_LEAF(S)_SUCCESS','ADD_LEAF(S)_FAILED'], - payload: { - request : { - url: `/leafs`, - method: 'post', - data: {leaf, additional}, - successMessage: "Successfully added the leaf(s)" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateLeaf(leafID, leaf) { - return { - types: ['NO_LOADING','UPDATE_LEAF_SUCCESS','UPDATE_LEAF_FAILED'], - payload: { - request : { - url: `/leafs/${leafID}`, - method: 'put', - data: {leaf}, - successMessage: "Successfully updated the leaf" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateLeafs(leafs, project_id) { - return { - types: ['NO_LOADING','UPDATE_LEAFS_SUCCESS','UPDATE_LEAFS_FAILED'], - payload: { - request : { - url: `/leafs`, - method: 'put', - data: {leafs, project_id}, - successMessage: "Successfully updated the leafs" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function conjoinLeafs(leafs) { - return { - types: ['NO_LOADING','UPDATE_LEAFS_SUCCESS','UPDATE_LEAFS_FAILED'], - payload: { - request : { - url: `/leafs/conjoin`, - method: 'put', - data: {leafs}, - successMessage: "Successfully conjoined the leafs" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteLeaf(leafID) { - return { - types: ['SHOW_LOADING','DELETE_LEAF_SUCCESS','DELETE_LEAF_FAILED'], - payload: { - request : { - url: `/leafs/${leafID}`, - method: 'delete', - successMessage: "Successfully deleted the leaf" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteLeafs(leafs) { - return { - types: ['SHOW_LOADING','DELETE_LEAFS_SUCCESS','DELETE_LEAFS_FAILED'], - payload: { - request : { - url: `/leafs`, - method: 'delete', - data: leafs, - successMessage: "Successfully deleted the leafs" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function addGroups(group, additional) { - return { - types: ['SHOW_LOADING','ADD_GROUP(S)_SUCCESS','ADD_GROUP(S)_FAILED'], - payload: { - request : { - url: `/groups`, - method: 'post', - data: {group, additional}, - successMessage: "Successfully added the groups" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateGroup(groupID, group) { - return { - types: ['NO_LOADING','UPDATE_GROUP_SUCCESS','UPDATE_GROUP_FAILED'], - payload: { - request : { - url: `/groups/${groupID}`, - method: 'put', - data: {group}, - successMessage: "Successfully updated the group" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateGroups(groups) { - return { - types: ['NO_LOADING','UPDATE_GROUPS_SUCCESS','UPDATE_GROUPS_FAILED'], - payload: { - request : { - url: `/groups`, - method: 'put', - data: {groups}, - successMessage: "Successfully updated the groups" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteGroup(groupID) { - return { - types: ['SHOW_LOADING','DELETE_GROUP_SUCCESS','DELETE_GROUP_FAILED'], - payload: { - request : { - url: `/groups/${groupID}`, - method: 'delete', - successMessage: "Successfully deleted the group" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteGroups(groups, projectID) { - return { - types: ['SHOW_LOADING','DELETE_GROUPS_SUCCESS','DELETE_GROUPS_FAILED'], - payload: { - request : { - url: `/groups`, - method: 'delete', - data: {...groups, projectID}, - successMessage: "Successfully deleted the groups" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateSide(sideID, side) { - return { - types: ['NO_LOADING','UPDATE_SIDE_SUCCESS','UPDATE_SIDE_FAILED'], - payload: { - request : { - url: `/sides/${sideID}`, - method: 'put', - data: {side}, - successMessage: "Successfully updated the side" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function updateSides(sides) { - return { - types: ['NO_LOADING','UPDATE_SIDES_SUCCESS','UPDATE_SIDES_FAILED'], - payload: { - request : { - url: `/sides`, - method: 'put', - data: {sides}, - successMessage: "Successfully updated the sides" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function addNote(note) { - /** - note: { - "project_id": "5951303fc9bf3c7b9a573a3f", - "title": "some title for note", - "type": "Ink", - "description": "blue ink" - } - */ - return { - types: ['SHOW_LOADING','CREATE_NOTE_SUCCESS','CREATE_NOTE_FAILED'], - 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: ['SHOW_LOADING','UPDATE_NOTE_SUCCESS','UPDATE_NOTE_FAILED'], - payload: { - request : { - url: `/notes/${noteID}`, - method: 'put', - data: {note}, - successMessage: "Successfully updated the note" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function getNotes(projectID) { - return { - types: ['NO_LOADING','LOAD_NOTES_SUCCESS','LOAD_NOTES_FAILED'], - payload: { - request : { - url: `/projects/${projectID}/notes`, - method: 'get', - successMessage: "Successfully loaded the notes" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteNote(noteID) { - return { - types: ['SHOW_LOADING','DELETE_NOTE_SUCCESS','DELETE_NOTE_FAILED'], - payload: { - request : { - url: `/notes/${noteID}`, - method: 'delete', - successMessage: "Successfully deleted the note" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function linkNote(noteID, objects) { - /** - objects: [ - { - "id": "5951303fc9bf3c7b9a573a3f", - "type": "Group" - }, - ] - */ - return { - types: ['NO_LOADING','LINK_NOTE_SUCCESS','LINK_NOTE_FAILED'], - payload: { - request : { - url: `/notes/${noteID}/link`, - method: 'put', - data: {objects}, - successMessage: "Successfully linked the note" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function unlinkNote(noteID, objects) { - /** - objects: [ - { - "id": "5951303fc9bf3c7b9a573a3f", - "type": "Group" - }, - ] - */ - return { - types: ['NO_LOADING','UNLINK_NOTE_SUCCESS','UNLINK_NOTE_FAILED'], - payload: { - request : { - url: `/notes/${noteID}/unlink`, - method: 'put', - data: {objects}, - successMessage: "Successfully unlinked the note" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function createNoteType(noteType) { - /** - "noteType": { - "project_id": "5951303fc9bf3c7b9a573a3f", - "type": "Ink" - } - */ - return { - types: ['NO_LOADING','CREATE_NOTETYPE_SUCCESS','CREATE_NOTETYPE_FAILED'], - payload: { - request : { - url: `/notes/type`, - method: 'post', - data: {noteType}, - successMessage: "Successfully created the note type" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function updateNoteType(noteType) { - /** - "noteType": { - "project_id": "5951303fc9bf3c7b9a573a3f", - "type": "Ink", - "old_type": "Inkss" - } - */ - return { - types: ['NO_LOADING','UPDATE_NOTETYPE_SUCCESS','UPDATE_NOTETYPE_FAILED'], - payload: { - request : { - url: `/notes/type`, - method: 'put', - data: {noteType}, - successMessage: "Successfully updated the note type" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function deleteNoteType(noteType) { - /** - "noteType": { - "project_id": "5951303fc9bf3c7b9a573a3f", - "type": "Ink" - } - */ - return { - types: ['NO_LOADING','DELETE_NOTETYPE_SUCCESS','DELETE_NOTETYPE_FAILED'], - payload: { - request : { - url: `/notes/type`, - method: 'delete', - data: {noteType}, - successMessage: "Successfully deleted the note type" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function mapSidesToImages(sideMappings) { - // sideMappings = [{id: 112, image: {}}, ...] - return { - types: ['SHOW_LOADING','MAP_SIDES_SUCCESS','MAP_SIDES_FAILED'], - payload: { - request : { - url: `/sides`, - method: 'put', - data: {sides: sideMappings}, - successMessage: "Successfully updated the sides" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} \ No newline at end of file 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..a2a5bea3 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/groupActions.js @@ -0,0 +1,123 @@ +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 + 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: "None", + type: "Quire", + tacketed: [], + 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 +} \ No newline at end of file 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..b9f776fe --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/imageActions.js @@ -0,0 +1,94 @@ +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" + }) + } +} + + +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(DIYImage => DIYImage.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..1c16e6b3 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/leafActions.js @@ -0,0 +1,246 @@ +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: "None", + type: "None", + attachment_method: "None", + conjoined_to: null, + attached_above: "None", + attached_below: "None", + 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.attached_above) { + state = handleAttachAbove(state, updatedLeafID, action.payload.request.data.leaf.attached_above); + } else if (action.payload.request.data.leaf.attached_below) { + state = handleAttachBelow(state, updatedLeafID, action.payload.request.data.leaf.attached_below); + } + // 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; +} + +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 } + } + 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 generateFolioNumbers(action, state) { + let folioNumberCount = 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]]; + recto.folio_number = folioNumberCount + recto.id[0]; + verso.folio_number = folioNumberCount + verso.id[0]; + folioNumberCount++; + } + 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..0d27ffe5 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/manifestActions.js @@ -0,0 +1,25 @@ +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..fa2d7616 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/noteActions.js @@ -0,0 +1,115 @@ +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..0e28a03a --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/projectActions.js @@ -0,0 +1,26 @@ +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 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..9e01c133 --- /dev/null +++ b/viscoll-app/src/actions/frontend/before/sideActions.js @@ -0,0 +1,69 @@ +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) + 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) + 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/projectActions.js b/viscoll-app/src/actions/projectActions.js deleted file mode 100644 index bdda5942..00000000 --- a/viscoll-app/src/actions/projectActions.js +++ /dev/null @@ -1,191 +0,0 @@ - -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: ['NO_LOADING','UPDATE_PROJECT_SUCCESS','UPDATE_PROJECT_FAILED'], - payload: { - request : { - url: `/projects/${projectID}`, - method: 'put', - data: {project}, - successMessage: "Successfully updated the project", - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - -export function deleteProject(projectID) { - return { - types: ['SHOW_LOADING','DELETE_PROJECT_SUCCESS','DELETE_PROJECT_FAILED'], - payload: { - request : { - url: `/projects/${projectID}`, - method: 'delete', - 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" - } - } - }; -} - - - -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 cloneProjectExport(projectID) { - return { - types: ['NO_LOADING','CLONE_PROJECT_EXPORT_SUCCESS','CLONE_PROJECT_EXPORT_FAILED'], - payload: { - request : { - url: `/projects/${projectID}/export/json`, - method: 'get', - successMessage: "" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function cloneProjectImport(data) { - return { - types: ['SHOW_LOADING','CLONE_PROJECT_IMPORT_SUCCESS','CLONE_PROJECT_IMPORT_FAILED'], - payload: { - request : { - url: `/projects/import`, - method: 'put', - data: data, - 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" - } - } - }; -} - - -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: ['SHOW_LOADING','UPDATE_MANIFEST_SUCCESS','UPDATE_MANIFEST_FAILED'], - payload: { - request : { - url: `/projects/${projectID}/manifests`, - method: 'put', - data: manifest, - successMessage: "You have successfully updated the manifest" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function deleteManifest(projectID, manifest) { - return { - types: ['SHOW_LOADING','DELETE_MANIFEST_SUCCESS','DELETE_MANIFEST_FAILED'], - payload: { - request : { - url: `/projects/${projectID}/manifests`, - method: 'delete', - data: manifest, - successMessage: "You have successfully deleted the manifest" , - errorMessage: "Ooops! Something went wrong" - } - } - }; -} - - -export function cancelCreateManifest(){ - return {type: "CANCEL_CREATE_MANIFEST"} -} \ No newline at end of file diff --git a/viscoll-app/src/assets/visualMode/PaperGroup.js b/viscoll-app/src/assets/visualMode/PaperGroup.js index a07589f9..2bfee108 100644 --- a/viscoll-app/src/assets/visualMode/PaperGroup.js +++ b/viscoll-app/src/assets/visualMode/PaperGroup.js @@ -30,13 +30,13 @@ PaperGroup.prototype = { if (this.group.nestLevel%2===0) { this.path.fillColor.brightness -= 0.05; } - this.path.name = "group " + this.group.order; + 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.group.order + " highlight"; + this.highlight.name = "group " + this.groupOrder + " highlight"; this.highlight.insertBelow(this.path); this.filterHighlight = new paper.Path.Rectangle(highlightRectangle); @@ -83,7 +83,7 @@ PaperGroup.prototype = { }, setVisibility: function(visibleAttributes) { this.visibleAttributes = visibleAttributes; - let groupText = this.group.type + " " + this.group.order; + let groupText = this.group.type + " " + this.groupOrder; if (this.visibleAttributes.title) groupText = groupText + ": " + this.group.title; this.text.set({ content: groupText, @@ -94,6 +94,7 @@ PaperGroup.prototype = { function PaperGroup(args) { this.manager = args.manager; this.group = args.group; + this.groupOrder = args.groupIDs.indexOf(args.group.id)+1 this.y = args.y; this.x = args.x; this.width = args.width; diff --git a/viscoll-app/src/assets/visualMode/PaperLeaf.js b/viscoll-app/src/assets/visualMode/PaperLeaf.js index f8190a68..36d27ced 100644 --- a/viscoll-app/src/assets/visualMode/PaperLeaf.js +++ b/viscoll-app/src/assets/visualMode/PaperLeaf.js @@ -28,7 +28,7 @@ PaperLeaf.prototype = { 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_leaf_order === "Binding" || this.leaf.conjoined_leaf_order === "None") { + if (this.leaf.conjoined_to === "None") { this.highlight.segments[0].point.x = this.highlight.segments[0].point.x - 5; } this.highlight.strokeColor = this.strokeColorActive; @@ -46,7 +46,7 @@ PaperLeaf.prototype = { 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_leaf_order === "Binding" || this.leaf.conjoined_leaf_order === "None") { + if (this.leaf.conjoined_to === "None") { this.filterHighlight.segments[0].point.x = this.filterHighlight.segments[0].point.x - 5; } this.filterHighlight.strokeColor = this.strokeColorFilter; @@ -58,8 +58,8 @@ PaperLeaf.prototype = { this.showAttributes(); this.createAttachments(); - const leafNotesToShow = this.leaf.notes.filter((noteID)=>{return this.Notes[noteID].show}); - const rectoNotesToShow = this.recto.notes.filter((noteID)=>{return this.Notes[noteID].show}); + 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; @@ -87,8 +87,9 @@ PaperLeaf.prototype = { // 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: "â–¼ " + this.recto.folio_number + " : " + note.title.substr(0,numChars), + content: noteTitle, point: [textX, textY - noteIndex*(this.spacing*0.7) - this.spacing*0.3], fillColor: this.strokeColor, fontSize: fontSize, @@ -103,7 +104,7 @@ PaperLeaf.prototype = { const note = this.Notes[leafNotesToShow[noteIndex]]; let textNote = new paper.PointText({ - content: "â–¼ L" + this.leaf.order + " : " + note.title.substr(0,numChars), + content: "â–¼ L" + this.order + " : " + note.title.substr(0,numChars), point: [textX, textY - rectoNotesToShow.length*(this.spacing*0.7) - noteIndex*(this.spacing*0.7) - this.spacing*0.3], fillColor: this.strokeColor, fontSize: fontSize, @@ -116,8 +117,9 @@ PaperLeaf.prototype = { // 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: "â–² " + this.verso.folio_number + " : " + note.title.substr(0,numChars), + content: noteTitle, point: [textX, this.y + noteIndex*(this.spacing*0.7) + this.spacing*0.8], fillColor: this.strokeColor, fontSize: fontSize, @@ -406,7 +408,10 @@ PaperLeaf.prototype = { this.showAttributes(); }, showAttributes: function() { + const rectoFolioNumber = this.recto.folio_number? this.recto.folio_number : " "; + const versoFolioNumber = this.verso.folio_number? this.verso.folio_number : " "; if (this.visibleAttributes.side.folio_number && this.visibleAttributes.side.texture) { + if (this.leaf.stub === "None") { // Reduce leaf width so we can fit attribute text this.path.segments[this.path.segments.length-1].point.x = this.width-this.spacing*2; @@ -414,8 +419,8 @@ PaperLeaf.prototype = { this.filterHighlight.segments[this.filterHighlight.segments.length-1].point.x = this.width-this.spacing*1.8; } this.textLeafOrder.set({point: [this.width-this.spacing*1.8, this.textLeafOrder.point.y]}); - this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: this.recto.folio_number + " " + this.recto.texture}); - this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: this.verso.folio_number + " " + this.verso.texture}); + this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: rectoFolioNumber + " " + this.recto.texture}); + this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: versoFolioNumber + " " + this.verso.texture}); } else if (this.visibleAttributes.side.folio_number) { if (this.leaf.stub === "None") { // Reduce leaf width so we can fit folio number text @@ -425,8 +430,8 @@ PaperLeaf.prototype = { } // Adjust text this.textLeafOrder.set({point: [this.width-this.spacing*0.80, this.textLeafOrder.point.y]}); - this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: this.recto.folio_number}); - this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: this.verso.folio_number}); + this.textRecto.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textRecto.point.y], content: rectoFolioNumber}); + this.textVerso.set({point:[this.textLeafOrder.bounds.right+this.spacing*0.2, this.textVerso.point.y], content: versoFolioNumber}); } else if (this.visibleAttributes.side.texture) { if (this.leaf.stub === "None") { // Reduce leaf width so we can fit texture text @@ -456,12 +461,13 @@ PaperLeaf.prototype = { 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 = this.leaf.order; - this.parentOrder = args.Groups[this.leaf.parentID].order; - this.conjoined_to = this.leaf.conjoined_leaf_order; + 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; diff --git a/viscoll-app/src/assets/visualMode/PaperManager.js b/viscoll-app/src/assets/visualMode/PaperManager.js index a87c3a79..063d6016 100644 --- a/viscoll-app/src/assets/visualMode/PaperManager.js +++ b/viscoll-app/src/assets/visualMode/PaperManager.js @@ -1,6 +1,7 @@ import paper from 'paper'; import PaperLeaf from "./PaperLeaf.js"; import PaperGroup from "./PaperGroup.js"; +import { getMemberOrder } from '../../helpers/getMemberOrder'; PaperManager.prototype = { constructor: PaperManager, @@ -8,7 +9,8 @@ PaperManager.prototype = { let g = new PaperGroup({ manager: this, group: group, - y: this.groupYs[group.order-1], + groupIDs: this.groupIDs, + y: this.groupYs[this.groupIDs.indexOf(group.id)], x: (group.nestLevel-1)*(this.spacing), width: this.width, groupHeight: this.getGroupHeight(group), @@ -34,7 +36,7 @@ PaperManager.prototype = { 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.order)) { + if (this.flashItems.groups.includes(group.id)) { this.flashGroups.push(g); } @@ -45,7 +47,7 @@ PaperManager.prototype = { origin: this.origin, width: this.width, spacing: this.spacing, - strokeWidth: this.strokeWidth * Math.min(1.0, this.multipliers[leaf.order]), + strokeWidth: this.strokeWidth * Math.min(1.0, this.multipliers[this.leafIDs.indexOf(leaf.id)+1]), strokeColor: this.strokeColor, strokeColorActive: this.strokeColorActive, strokeColorGroupActive: this.strokeColorGroupActive, @@ -57,11 +59,11 @@ PaperManager.prototype = { leafIDs: this.leafIDs, Groups: this.Groups, Notes: this.Notes, - y: this.leafYs[leaf.order-1], + 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[leaf.order], + multiplier: this.multipliers[this.leafIDs.indexOf(leaf.id)+1], strokeColorFilter: this.strokeColorFilter, visibleAttributes: this.visibleAttributes, viewingMode: this.viewingMode, @@ -74,7 +76,7 @@ PaperManager.prototype = { this.groupLeaves.addChild(l.textVerso); this.groupLeaves.addChild(l.attachment); this.groupLeaves.addChild(l.textNotes); - if (this.flashItems.leaves.includes(leaf.order)) { + if (this.flashItems.leaves.includes(leaf.id)) { this.flashLeaves.push(l); } return l; @@ -94,15 +96,23 @@ PaperManager.prototype = { // Calculate y positions of groups and leaves let currentY = 0; - for (let groupID of this.groupIDs) { + 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); - currentY = currentY + this.spacing; + if (group.memberIDs.length===0) { - currentY = currentY + this.spacing/2.0; + currentY = currentY + this.spacing; } + prevRootGroupID = groupID; } } @@ -144,6 +154,7 @@ PaperManager.prototype = { this.groupTacketGuideLine.removeChildren(); this.tacketToolIsActive = true; + if (this.tool) this.tool.remove(); this.tool = new paper.Tool(); this.tool.minDistance=5; let targets = []; @@ -154,7 +165,7 @@ PaperManager.prototype = { document.body.style.cursor = "crosshair"; this.drawTacketGuide(groupID, type); - + this.tool.onMouseDown = (event) => { this.tacketLineDrag = new paper.Path(); this.tacketLineDrag.strokeColor = this.strokeColorTacket; @@ -196,7 +207,7 @@ PaperManager.prototype = { 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.Leafs[memberID].order); + 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"; @@ -210,6 +221,7 @@ PaperManager.prototype = { }); } + this.tool.activate(); }, drawTacketGuide: function(groupID, type) { const targetGroup = this.paperGroups.find((member)=>{return (member.group.id===groupID)}); @@ -280,9 +292,9 @@ PaperManager.prototype = { let startX, startY, endX, endY; let paperLeaf1, paperLeaf2; - paperLeaf1 = this.getLeaf(this.Leafs[leafID1].order); + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(this.Leafs[leafID1].id)+1); if (leafID2!==undefined) { - paperLeaf2 = this.getLeaf(this.Leafs[leafID2].order); + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(this.Leafs[leafID2].id)+1); startX = paperLeaf1.path.segments[0].point.x-this.strokeWidth; startY = paperLeaf2.path.segments[0].point.y; endX = paperLeaf2.path.segments[0].point.x; @@ -326,9 +338,9 @@ PaperManager.prototype = { let startX, startY, endX, endY; let paperLeaf1, paperLeaf2; - paperLeaf1 = this.getLeaf(this.Leafs[leafID1].order); + paperLeaf1 = this.getLeaf(this.leafIDs.indexOf(this.Leafs[leafID1].id)+1); if (leafID2!==undefined) { - paperLeaf2 = this.getLeaf(this.Leafs[leafID2].order); + paperLeaf2 = this.getLeaf(this.leafIDs.indexOf(this.Leafs[leafID2].id)+1); startX = paperLeaf1.path.segments[0].point.x-this.strokeWidth; startY = paperLeaf2.path.segments[0].point.y; endX = paperLeaf2.path.segments[0].point.x; @@ -387,7 +399,7 @@ PaperManager.prototype = { getYOfFirstMember: function(groupID) { let group = this.Groups[groupID]; if (group.memberIDs.length===0) { - let y = this.groupYs[group.order-1]; + let y = this.groupYs[this.groupIDs.indexOf(group.id)]; return y; } let firstMemberID = group.memberIDs[0]; @@ -395,7 +407,7 @@ PaperManager.prototype = { if (firstMemberID.memberType==="Group") { return this.getYOfFirstMember(firstMemberID); } else { - let firstLeafY = this.leafYs[firstMember.order-1]; + let firstLeafY = this.leafYs[this.leafIDs.indexOf(firstMember.id)]; return firstLeafY; } }, @@ -403,32 +415,32 @@ PaperManager.prototype = { const group = this.Groups[groupID]; const lastMember = this.getLastMember(groupID); if (lastMember && lastMember.memberType==="Group") { - let y = this.groupYs[lastMember.order-1]; - return y+((lastMember.nestLevel-group.nestLevel)*this.spacing + (this.spacing)); + 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[lastMember.order-1] + this.strokeWidth + this.spacing/2.0; + 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 lastMember = null; - for (let memberID of this.Groups[groupID].memberIDs) { - let memberObject = this[memberID.split("_")[0]+"s"][memberID]; - if (lastMember===null || (memberObject.memberOrder>lastMember.memberOrder)) { - lastMember = memberObject; - } - if (memberID.charAt(0)==="G" && memberObject.memberIDs.length>0) { - let result = this.getLastMember(memberID); - if (result) lastMember = result; - } + 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; } - return lastMember; }, getGroupHeight: function(group) { if (group.memberIDs.length>0) { - let height = this.getYOfLastMember(group.id) - this.groupYs[group.order-1]; + let height = this.getYOfLastMember(group.id) - this.groupYs[this.groupIDs.indexOf(group.id)]; return height+this.spacing; } else { return this.spacing; @@ -468,51 +480,39 @@ PaperManager.prototype = { glueSpacing = (notesToShowAbove>0 && memberObject.attached_above.includes("Glued") && !memberObject.attached_above.includes("Partial"))? 1 : 0; } - if (memberObject.memberType==="Leaf" && memberObject.memberOrder===1 && notesToShowAbove>0) { - // First leaf in the group with a note - this.multipliers[memberObject.order] = multiplier; + 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); - if (i > 0 && members[i-1].memberType==="Group" && members[i-1].memberIDs.length) { - // Previous sibling is a group with children - currentY = currentY - memberObject.nestLevel*spacing; - } 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" && memberObject.order > 0) { - this.multipliers[memberObject.order] = multiplier; + } 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].memberType==="Group" && this.Groups[members[i-1]].memberIDs.length) { + if (i > 0 && members[i-1].includes("Group") && this.Groups[members[i-1]].memberIDs.length) { // Previous sibling is a group with children - currentY = currentY - memberObject.nestLevel*spacing; + // 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; - - if (i===members.length-1) { - // Last member of group - currentY = currentY + (memberObject.nestLevel)*spacing/4; - } } else if (memberObject.memberType==="Group") { currentY = currentY + spacing; - if (i > 0 && members[i-1].memberType==="Group" && this.Groups[members[i-1]].memberIDs.length>0) { - currentY = currentY - memberObject.nestLevel*spacing; - } - this.groupYs.push(currentY); - if (memberObject.memberIDs.length<1) { - // No nested members, so give padding equal to - // the height of this empty group, which is 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; - if (i===members.length-1) { - // If we are the last member and it's empty group - currentY = currentY + (memberObject.nestLevel)*spacing; - } } + this.groupYs.push(currentY); + // Recursify!!! currentY = this.calculateYs(memberObject.memberIDs, currentY, spacing); } @@ -673,6 +673,7 @@ function PaperManager(args) { this.tacketToolOriginalPosition = 0; this.slideForward = true; this.openNoteDialog = args.openNoteDialog; + this.leafIDs = args.leafIDs; let that = this; // Flash newly added items diff --git a/viscoll-app/src/components/authentication/Login.js b/viscoll-app/src/components/authentication/Login.js index 93a4375a..dd1b03e7 100644 --- a/viscoll-app/src/components/authentication/Login.js +++ b/viscoll-app/src/components/authentication/Login.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import ResendConfirmation from './ResendConfirmation'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; @@ -100,19 +99,6 @@ class Login extends Component { content ); } - static propTypes = { - /** History object provided by react router. */ - history: PropTypes.object, - /** User object from the store. */ - user: PropTypes.object, - /** Dictionary of actions. */ - action: PropTypes.objectOf(PropTypes.func), - /** Cancel callback to close this component. */ - tapCancel: PropTypes.func, - /** Callback to show the reset password form. */ - toggleResetRequest: PropTypes.func, - } - } export default Login; diff --git a/viscoll-app/src/components/authentication/Login.md b/viscoll-app/src/components/authentication/Login.md deleted file mode 100644 index 7f5e15c2..00000000 --- a/viscoll-app/src/components/authentication/Login.md +++ /dev/null @@ -1,7 +0,0 @@ - -##### LOCAL STATES - -| Name | Type | Description | -|---|---|---| -| email | string | Stores current value of the email input field | -| password | string | Stores current value of the password input field | diff --git a/viscoll-app/src/components/authentication/Register.js b/viscoll-app/src/components/authentication/Register.js index 5e4fbc1b..134a82de 100644 --- a/viscoll-app/src/components/authentication/Register.js +++ b/viscoll-app/src/components/authentication/Register.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; @@ -115,14 +114,6 @@ class Register extends Component { return registerForm; } } - static propTypes = { - /** User object from the store. */ - userState: PropTypes.object, - /** Dictionary of actions. */ - action: PropTypes.objectOf(PropTypes.func), - /** Cancel callback to close this component. */ - tapCancel: PropTypes.func, - } } export default Register; diff --git a/viscoll-app/src/components/authentication/Register.md b/viscoll-app/src/components/authentication/Register.md deleted file mode 100644 index d4ff5fcb..00000000 --- a/viscoll-app/src/components/authentication/Register.md +++ /dev/null @@ -1,8 +0,0 @@ - -##### LOCAL STATES - -| Name | Type | Description | -|---|---|---| -| name | string | Stores current value of the name input field | -| email | string | Stores current value of the email input field | -| password | string | Stores current value of the password input field | diff --git a/viscoll-app/src/components/authentication/ResetPassword.js b/viscoll-app/src/components/authentication/ResetPassword.js index fbb2445c..9f90dc49 100644 --- a/viscoll-app/src/components/authentication/ResetPassword.js +++ b/viscoll-app/src/components/authentication/ResetPassword.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import { btnLg } from '../../styles/button'; @@ -71,15 +70,6 @@ class ResetPassword extends Component { ); } - - static propTypes = { - /** Callback function to submit password change. */ - resetPassword: PropTypes.func, - /** Reset password token. */ - reset_password_token: PropTypes.string, - /** Success callback to close this component. */ - handleResetPasswordSuccess: PropTypes.func, - } } export default ResetPassword; diff --git a/viscoll-app/src/components/authentication/ResetPassword.md b/viscoll-app/src/components/authentication/ResetPassword.md deleted file mode 100644 index 78797bcb..00000000 --- a/viscoll-app/src/components/authentication/ResetPassword.md +++ /dev/null @@ -1,9 +0,0 @@ - -##### LOCAL STATES - -| Name | Type | Description | -|---|---|---| -| password | string | Stores current value of the password input field | -| passwordConfirm | string | Stores current value of the password confirm input field | -| resetMessage | string | Stores current message to display above the form | - diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.js b/viscoll-app/src/components/authentication/ResetPasswordRequest.js index 479ec152..e090570c 100644 --- a/viscoll-app/src/components/authentication/ResetPasswordRequest.js +++ b/viscoll-app/src/components/authentication/ResetPasswordRequest.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import TextField from 'material-ui/TextField'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; @@ -91,11 +90,5 @@ class ResetPasswordRequest extends Component { ) } - static propTypes = { - /** Dictionary of actions. */ - action: PropTypes.objectOf(PropTypes.func), - /** Cancel callback to close this component. */ - tapCancel: PropTypes.func, - } } export default ResetPasswordRequest; diff --git a/viscoll-app/src/components/authentication/ResetPasswordRequest.md b/viscoll-app/src/components/authentication/ResetPasswordRequest.md deleted file mode 100644 index ea0c70d1..00000000 --- a/viscoll-app/src/components/authentication/ResetPasswordRequest.md +++ /dev/null @@ -1,7 +0,0 @@ -##### LOCAL STATES - -| Name | Type | Description | -|---|---|---| -| email | string | Stores current value of the email input field | -| resetMessage | string | Stores current message to display above the form | -| requested | boolean | Value is `true` if the user pressed the submit button. This state is used to know when to show a confirmation message upon user submit. | diff --git a/viscoll-app/src/components/collationManager/TabularMode.js b/viscoll-app/src/components/collationManager/TabularMode.js index ddcb646e..81425f63 100644 --- a/viscoll-app/src/components/collationManager/TabularMode.js +++ b/viscoll-app/src/components/collationManager/TabularMode.js @@ -1,6 +1,5 @@ import React from 'react'; import update from 'immutability-helper'; -import PropTypes from 'prop-types'; import Group from './tabularMode/Group'; @@ -39,6 +38,7 @@ export default class TabularMode extends React.Component { ); } @@ -64,9 +65,3 @@ export default class TabularMode extends React.Component { ); } } - -TabularMode.propTypes = { - /** Callback for handling clicking on an object (group or leaf) */ - handleObjectClick: PropTypes.func, -} - diff --git a/viscoll-app/src/components/collationManager/ViewingMode.js b/viscoll-app/src/components/collationManager/ViewingMode.js index 0897908e..2fd8ba1d 100644 --- a/viscoll-app/src/components/collationManager/ViewingMode.js +++ b/viscoll-app/src/components/collationManager/ViewingMode.js @@ -118,22 +118,21 @@ export default class ViewingMode extends React.Component { }; - let leafID, rectoURL, versoURL; + let leafID, leaf, recto, verso, isRectoDIY, isVersoDIY, rectoURL, versoURL; if (this.props.selectedObjects.type==="Leaf"){ leafID = this.props.selectedObjects.members[0]; - const leaf = this.props.project.Leafs[leafID]; - const recto = this.props.project.Rectos[leaf.rectoID]; - const verso = this.props.project.Versos[leaf.versoID]; - rectoURL = recto.image.url; - versoURL = verso.image.url; + 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") { - const recto = this.props.project.Rectos[this.props.selectedObjects.members[0]]; - rectoURL = recto.image.url; + recto = this.props.project.Rectos[this.props.selectedObjects.members[0]]; } else if (this.props.selectedObjects.type==="Verso") { - const verso = this.props.project.Versos[this.props.selectedObjects.members[0]]; - versoURL = verso.image.url; + 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 (
@@ -141,7 +140,7 @@ export default class ViewingMode extends React.Component {
{this.props.imageViewerEnabled? - + :"" }
diff --git a/viscoll-app/src/components/collationManager/VisualMode.js b/viscoll-app/src/components/collationManager/VisualMode.js index 1a9b043a..1b6163e2 100644 --- a/viscoll-app/src/components/collationManager/VisualMode.js +++ b/viscoll-app/src/components/collationManager/VisualMode.js @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import PaperManager from "../../assets/visualMode/PaperManager.js"; /** Contains the collation drawing in a canvas element */ @@ -135,18 +134,3 @@ export default class VisualMode extends React.Component { ); } } -VisualMode.propTypes = { - /** Array of root group objects */ - groups: PropTypes.arrayOf(PropTypes.object), - /** Callback for handling clicking on an object (group or leaf) */ - handleObjectClick: PropTypes.func, - /** Dictionary of selected objects */ - selectedObjects: PropTypes.object, - /** Dictionary containing arrays of updated leaf/group ID's to 'flash' - from Redux store */ - flashItems: PropTypes.shape({ - leaves: PropTypes.arrayOf(PropTypes.number), - groups: PropTypes.arrayOf(PropTypes.number) - }), - /** Dictionary of filter matches */ - filters: PropTypes.object, -} diff --git a/viscoll-app/src/components/collationManager/dialog/NoteDialog.js b/viscoll-app/src/components/collationManager/dialog/NoteDialog.js index 48cd289d..87dfd986 100644 --- a/viscoll-app/src/components/collationManager/dialog/NoteDialog.js +++ b/viscoll-app/src/components/collationManager/dialog/NoteDialog.js @@ -11,7 +11,7 @@ export default class NoteDialog extends React.Component { return (this.props.Groups[groupID].notes.includes(this.props.activeNote.id)) }); return groupsWithCurrentNote.map((value) => { - const label = `Group ${this.props.Groups[value].order}`; + const label = `Group ${this.props.groupIDs.indexOf(value)+1}`; return {label, value}; }); } @@ -21,7 +21,7 @@ export default class NoteDialog extends React.Component { return (this.props.Leafs[leafID].notes.includes(this.props.activeNote.id)) }); return leafsWithCurrentNote.map((value)=>{ - const label = `Leaf ${this.props.Leafs[value].order}`; + const label = `Leaf ${this.props.leafIDs.indexOf(value)+1}`; return {label, value}; }); } @@ -35,13 +35,13 @@ export default class NoteDialog extends React.Component { }); const sidesWithCurrentNote = []; for (let value of rectosWithCurrentNote){ - const leafOrder = this.props.Leafs[this.props.Rectos[value].parentID].order; - const label = `Leaf ${leafOrder}: Side Recto}`; + const leafOrder = this.props.leafIDs.indexOf(this.props.Rectos[value].parentID) + 1; + const label = `L${leafOrder} Recto (${this.props.Rectos[value].folio_number})`; sidesWithCurrentNote.push({label, value}) } for (let value of versosWithCurrentNote){ - const leafOrder = this.props.Leafs[this.props.Versos[value].parentID].order; - const label = `Leaf ${leafOrder}: Side Verso}`; + const leafOrder = this.props.leafIDs.indexOf(this.props.Versos[value].parentID) + 1; + const label = `L${leafOrder} Verso (${this.props.Versos[value].folio_number})`; sidesWithCurrentNote.push({label, value}) } return sidesWithCurrentNote; @@ -101,6 +101,11 @@ export default class NoteDialog extends React.Component { linkedLeaves={this.getLinkedLeaves()} linkedSides={this.getLinkedSides()} isReadOnly={this.props.isReadOnly} + groupIDs={this.props.groupIDs} + leafIDs={this.props.leafIDs} + rectoIDs={this.props.rectoIDs} + versoIDs={this.props.versoIDs} + togglePopUp={this.props.togglePopUp} />
); diff --git a/viscoll-app/src/components/collationManager/tabularMode/Group.js b/viscoll-app/src/components/collationManager/tabularMode/Group.js index 87d3b3b5..c017a939 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Group.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Group.js @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import Leaf from './Leaf'; import IconButton from 'material-ui/IconButton'; import ExpandMore from 'material-ui/svg-icons/navigation/expand-more'; @@ -35,6 +34,7 @@ export default class Group extends React.Component { ); } else { @@ -51,6 +52,7 @@ export default class Group extends React.Component { ); } @@ -89,7 +92,7 @@ export default class Group extends React.Component { activeGroupStyle["borderColor"] = "#d9dbdb"; } let groupContainerClasses = "groupContainer "; - if (this.props.collationManager.flashItems.groups.includes(this.props.activeGroup.order)) groupContainerClasses += "flash "; + 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 "; @@ -103,9 +106,9 @@ export default class Group extends React.Component {
- Group {this.props.activeGroup.order} + Group {this.props.activeGroupOrder} {if(e.key===" "){this.props.handleObjectPress(this.props.activeGroup, e)}}} @@ -118,7 +121,7 @@ export default class Group extends React.Component {
{e.stopPropagation();e.preventDefault();this.handleChange("open", !this.state.open)}} - aria-label={this.state.open?"Collapse group " + this.props.activeGroup.order : "Expand group " + this.props.activeGroup.order } + 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"} > @@ -139,12 +142,6 @@ export default class Group extends React.Component { ); } } -Group.propTypes = { - /** Group object */ - activeGroup: PropTypes.object, - /** Callback for handling clicking on an object (group or leaf) */ - handleObjectClick: PropTypes.func, -} \ 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 index b90a924c..7ce34cfb 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Leaf.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Leaf.js @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import Side from './Side'; /** Stateless functional component that displays one leaf in the tabular edit mode. */ @@ -37,14 +36,25 @@ const Leaf = (props) => { if (visibleAttributes.leaf[attributeName]) { let divStyle = "attribute "; if (isActive) divStyle += "active "; + if (attributeName==="conjoined_to"){ leafAttributes.push( -
+
- {leafAttribute.displayName} - {activeLeaf[attributeName]} + {leafAttribute.displayName} + {props.leafIDs.indexOf(activeLeaf[attributeName])!==-1 ? `Leaf ${props.leafIDs.indexOf(activeLeaf[attributeName])+1}` : "None"}
); + } else{ + leafAttributes.push( +
+
+ {leafAttribute.displayName} + {activeLeaf[attributeName]} +
+
+ ); + } } } @@ -71,6 +81,7 @@ const Leaf = (props) => { { { if (props.focusLeafID === activeLeaf.id) sectionStyle += "focus"; let leafComponent =
{ onMouseLeave={()=>props.toggleFocusLeaf(null)} >
- Leaf {activeLeaf.order} + Leaf {props.activeLeafOrder} {if(e.key===" "){props.handleObjectPress(activeLeaf, e)}}} @@ -135,11 +147,4 @@ const Leaf = (props) => { leafComponent ); } -Leaf.propTypes = { - /** Leaf object */ - activeLeaf: PropTypes.object, - /** Callback for handling clicking on an object (group or leaf) */ - handleObjectClick: PropTypes.func, -} - export default Leaf; diff --git a/viscoll-app/src/components/collationManager/tabularMode/Side.js b/viscoll-app/src/components/collationManager/tabularMode/Side.js index a8bdf759..02ec8122 100644 --- a/viscoll-app/src/components/collationManager/tabularMode/Side.js +++ b/viscoll-app/src/components/collationManager/tabularMode/Side.js @@ -19,14 +19,13 @@ const Side = (props) => { let sideAttributes = []; for (let attribute of defaultAttributes.side) { - let attributeName = attribute.name; - if (visibleAttributes.side[attributeName]) { + if (visibleAttributes.side[attribute.name]) { sideAttributes.push(
- {attribute.displayName}: {activeSide[attributeName]} + {attribute.displayName}: {activeSide[attribute.name]}
); } @@ -60,7 +59,7 @@ const Side = (props) => { > {activeSideName.charAt(0)} {if(e.key===" "){props.handleObjectPress(activeSide, e)}}} @@ -82,7 +81,7 @@ const Side = (props) => {
{activeSideName} {if(e.key===" "){props.handleObjectPress(activeSide, e)}}} diff --git a/viscoll-app/src/components/dashboard/CloneProject.js b/viscoll-app/src/components/dashboard/CloneProject.js index eb8678ab..8d428c1d 100644 --- a/viscoll-app/src/components/dashboard/CloneProject.js +++ b/viscoll-app/src/components/dashboard/CloneProject.js @@ -2,9 +2,7 @@ import React from 'react'; import {floatFieldLight} from '../../styles/textfield'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; -import SelectField from 'material-ui/SelectField'; -import MenuItem from 'material-ui/MenuItem'; - +import SelectField from '../global/SelectField'; export default class CloneProject extends React.Component { @@ -15,7 +13,7 @@ export default class CloneProject extends React.Component { } } - onChange = (event, projectIndex) => { + onChange = (projectIndex) => { this.setState({ projectIndex }); } @@ -28,30 +26,22 @@ export default class CloneProject extends React.Component { 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.allProjects.map((project, index)=>{ - return ( - - ); - })} - +
{ const errors = {title:"", shelfmark:"", date:""}; const allProjectsExceptCurrent = [...this.state.allProjects]; - allProjectsExceptCurrent.splice(this.state.selectedProjectIndex, 1); + 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) @@ -129,6 +130,7 @@ class EditProjectForm extends React.Component { * @public */ handleDeleteDialogToggle = (deleteDialog=false) => { + this.props.togglePopUp(deleteDialog); this.setState({ deleteDialog }); }; @@ -172,11 +174,7 @@ class EditProjectForm extends React.Component { this.props.closeProjectPanel(); this.setState({deleteDialog: false}); const projectID = this.props.selectedProject.id; - const user = { - id: this.props.user.id, - token: this.props.user.token - }; - this.props.deleteProject(projectID, user); + this.props.deleteProject(projectID, this.state.deleteUnlinkedImages); }; /** @@ -335,7 +333,7 @@ class EditProjectForm extends React.Component { />, ]; - + return (
@@ -374,7 +372,13 @@ class EditProjectForm extends React.Component { modal={false} open={this.state.deleteDialog} onRequestClose={this.handleDeleteDialogToggle} + > + this.setState({deleteUnlinkedImages: !this.state.deleteUnlinkedImages})} /> +
); } - static propTypes = { - /** Array of projects belonging to the user. */ - allProjects: PropTypes.array, - /** Currently selected project object. */ - selectedProject: PropTypes.shape({ - created_at: PropTypes.string, - updated_at: PropTypes.string, - id: PropTypes.string, - title: PropTypes.string, - }), - /** Index of the selected project in the list of projects belonging to the user. */ - selectedProjectIndex: PropTypes.number, - /** Callback to close the project panel. */ - closeProjectPanel: PropTypes.func, - /** Callback to update a project. */ - updateProject: PropTypes.func, - /** Callback to delete a project. */ - deleteProject: PropTypes.func, - /** User object */ - user: PropTypes.object, - - } } diff --git a/viscoll-app/src/components/dashboard/EditProjectForm.md b/viscoll-app/src/components/dashboard/EditProjectForm.md deleted file mode 100644 index 3c9a82ea..00000000 --- a/viscoll-app/src/components/dashboard/EditProjectForm.md +++ /dev/null @@ -1,9 +0,0 @@ - -##### LOCAL STATES - -| Name | Type | Description | -|---|---|---| -| unsavedDialog | boolean | The dialog to alert of unsaved changes will appear if this variable is set to `true` | -| deleteDialog | boolean | The dialog confirm project deletion will appear if this variable is set to `true` | -| editing | object | Is `true` if there are unsaved changes in any input fields
title: boolean
shelfmark: boolean
uri: boolean
date: boolean| - diff --git a/viscoll-app/src/components/dashboard/ImageCollections.js b/viscoll-app/src/components/dashboard/ImageCollections.js new file mode 100644 index 00000000..b7304bf0 --- /dev/null +++ b/viscoll-app/src/components/dashboard/ImageCollections.js @@ -0,0 +1,308 @@ +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'; + +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 + * @param {array} list1 + * @param {array} list2 + * @public + */ + 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 index 062dc796..35d41686 100644 --- a/viscoll-app/src/components/dashboard/ImportProject.js +++ b/viscoll-app/src/components/dashboard/ImportProject.js @@ -11,10 +11,10 @@ export default class ImportProject extends React.Component { this.state = { importData: "", importFormat: "json", + imageData:"", } } - isDisabled = () => { if (this.state.importData) return (this.state.importData.length===0); @@ -25,7 +25,7 @@ export default class ImportProject extends React.Component { submit = (event) => { if (event) event.preventDefault(); if (!this.isDisabled()) { - this.props.importProject({importData: this.state.importData, importFormat: this.state.importFormat}); + this.props.importProject({importData: this.state.importData, importFormat: this.state.importFormat, imageData: this.state.imageData}); } } @@ -34,13 +34,12 @@ export default class ImportProject extends React.Component { } componentWillReceiveProps = (nextProps) => { - if (nextProps.importStatus==="SUCCESS"){ + if (nextProps.importStatus==="SUCCESS") { nextProps.reset(); nextProps.close(); } } - checkIfFileTypeIsInvalid = (file) => { const allowedFileTypes = ["json", "xml"]; return !allowedFileTypes.includes(file.type) @@ -55,26 +54,28 @@ export default class ImportProject extends React.Component { 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

-

Please paste the content of your exported collation data in the textbox below, or upload a file to import.

+

Import collation

+

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

-