blacklight-spotlight 0.32.0 → 0.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/app/assets/images/spotlight/default_browse_thumbnail.jpg +0 -0
  4. data/app/assets/javascripts/spotlight/application.js +6 -1
  5. data/app/assets/javascripts/spotlight/blocks/pages_block.js +1 -1
  6. data/app/assets/javascripts/spotlight/blocks/resources_block.js +7 -4
  7. data/app/assets/javascripts/spotlight/blocks/solr_documents_base_block.js +108 -0
  8. data/app/assets/javascripts/spotlight/blocks/solr_documents_block.js +12 -56
  9. data/app/assets/javascripts/spotlight/blocks/solr_documents_carousel_block.js +3 -3
  10. data/app/assets/javascripts/spotlight/blocks/solr_documents_embed_block.js +3 -3
  11. data/app/assets/javascripts/spotlight/blocks/solr_documents_features_block.js +3 -3
  12. data/app/assets/javascripts/spotlight/blocks/solr_documents_grid_block.js +3 -3
  13. data/app/assets/javascripts/spotlight/crop.es6 +205 -0
  14. data/app/assets/javascripts/spotlight/croppable.js +7 -104
  15. data/app/assets/javascripts/spotlight/iiif.es6 +54 -0
  16. data/app/assets/javascripts/spotlight/multi_image_selector.js +34 -16
  17. data/app/assets/javascripts/spotlight/pages.js.erb +1 -1
  18. data/app/assets/javascripts/spotlight/reindex_monitor.js +5 -1
  19. data/app/assets/javascripts/spotlight/search_typeahead.js +33 -47
  20. data/app/assets/javascripts/spotlight/sir-trevor/locales.js +5 -2
  21. data/app/assets/javascripts/spotlight/zpr_links.js.erb +30 -0
  22. data/app/assets/stylesheets/spotlight/_croppable.scss +8 -13
  23. data/app/assets/stylesheets/spotlight/_multi_image_selector.scss +1 -1
  24. data/app/assets/stylesheets/spotlight/_pages.scss +5 -0
  25. data/app/assets/stylesheets/spotlight/typeahead.css +0 -1
  26. data/app/controllers/concerns/spotlight/base.rb +3 -5
  27. data/app/controllers/spotlight/appearances_controller.rb +6 -4
  28. data/app/controllers/spotlight/catalog_controller.rb +10 -0
  29. data/app/controllers/spotlight/contacts_controller.rb +2 -6
  30. data/app/controllers/spotlight/featured_images_controller.rb +26 -0
  31. data/app/controllers/spotlight/pages_controller.rb +3 -2
  32. data/app/controllers/spotlight/resources/iiif_harvester_controller.rb +10 -0
  33. data/app/controllers/spotlight/resources/upload_controller.rb +4 -3
  34. data/app/controllers/spotlight/searches_controller.rb +20 -6
  35. data/app/controllers/spotlight/sites_controller.rb +2 -5
  36. data/app/helpers/spotlight/application_helper.rb +14 -1
  37. data/app/helpers/spotlight/crop_helper.rb +37 -0
  38. data/app/helpers/spotlight/main_app_helpers.rb +13 -0
  39. data/app/helpers/spotlight/meta_helper.rb +10 -20
  40. data/app/models/concerns/spotlight/solr_document.rb +1 -2
  41. data/app/models/concerns/spotlight/solr_document/uploaded_resource.rb +1 -23
  42. data/app/models/sir_trevor_rails/blocks/browse_block.rb +10 -0
  43. data/app/models/sir_trevor_rails/blocks/featured_pages_block.rb +10 -0
  44. data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +5 -0
  45. data/app/models/spotlight/analytics/ga.rb +1 -1
  46. data/app/models/spotlight/blacklight_configuration.rb +16 -8
  47. data/app/models/spotlight/contact.rb +2 -13
  48. data/app/models/spotlight/contact_image.rb +11 -0
  49. data/app/models/spotlight/exhibit.rb +11 -8
  50. data/app/models/spotlight/exhibit_thumbnail.rb +12 -0
  51. data/app/models/spotlight/feature_page.rb +3 -5
  52. data/app/models/spotlight/featured_image.rb +28 -9
  53. data/app/models/spotlight/home_page.rb +2 -0
  54. data/app/models/spotlight/masthead.rb +5 -11
  55. data/app/models/spotlight/page.rb +5 -0
  56. data/app/models/spotlight/reindex_progress.rb +10 -18
  57. data/app/models/spotlight/reindexing_log_entry.rb +1 -0
  58. data/app/models/spotlight/resources/iiif_harvester.rb +33 -0
  59. data/app/models/spotlight/resources/iiif_manifest.rb +211 -0
  60. data/app/models/spotlight/resources/iiif_service.rb +93 -0
  61. data/app/models/spotlight/resources/upload.rb +1 -2
  62. data/app/models/spotlight/search.rb +5 -34
  63. data/app/presenters/spotlight/iiif_manifest_presenter.rb +79 -0
  64. data/app/serializers/spotlight/exhibit_export_serializer.rb +9 -41
  65. data/app/services/spotlight/carrierwave_file_resolver.rb +3 -1
  66. data/app/services/spotlight/iiif_resource_resolver.rb +73 -0
  67. data/app/services/spotlight/resources/iiif_builder.rb +17 -0
  68. data/app/services/spotlight/upload_solr_document_builder.rb +23 -23
  69. data/app/uploaders/spotlight/attachment_uploader.rb +0 -48
  70. data/app/uploaders/spotlight/featured_image_uploader.rb +2 -16
  71. data/app/views/_user_util_links.html.erb +8 -5
  72. data/app/views/catalog/_save_search.html.erb +4 -2
  73. data/app/views/layouts/spotlight/spotlight.html.erb +5 -1
  74. data/app/views/shared/_masthead.html.erb +1 -1
  75. data/app/views/spotlight/about_pages/_contact_properties.html.erb +1 -1
  76. data/app/views/spotlight/appearances/edit.html.erb +26 -6
  77. data/app/views/spotlight/browse/_search.html.erb +1 -1
  78. data/app/views/spotlight/contacts/_form.html.erb +12 -7
  79. data/app/views/spotlight/dashboards/_reindexing_activity.html.erb +1 -1
  80. data/app/views/spotlight/exhibits/_exhibit_card_front.html.erb +2 -2
  81. data/app/views/spotlight/featured_images/_form.html.erb +12 -15
  82. data/app/views/spotlight/featured_images/_upload_form.html.erb +6 -12
  83. data/app/views/spotlight/metadata_configurations/_metadata_field.html.erb +3 -3
  84. data/app/views/spotlight/pages/_form.html.erb +3 -3
  85. data/app/views/spotlight/pages/edit.html.erb +4 -2
  86. data/app/views/spotlight/resources/iiif/_form.html.erb +9 -0
  87. data/app/views/spotlight/searches/_form.html.erb +3 -3
  88. data/app/views/spotlight/searches/_search.html.erb +4 -2
  89. data/app/views/spotlight/sir_trevor/blocks/_browse_block.html.erb +2 -2
  90. data/app/views/spotlight/sir_trevor/blocks/_featured_pages_block.html.erb +1 -1
  91. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_block.html.erb +7 -2
  92. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +4 -2
  93. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_features_block.html.erb +4 -2
  94. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_grid_block.html.erb +4 -2
  95. data/app/views/spotlight/sites/edit.html.erb +1 -1
  96. data/config/locales/spotlight.en.yml +31 -6
  97. data/config/routes.rb +9 -0
  98. data/db/migrate/20160714144125_add_iiif_urls_to_featured_image.rb +9 -0
  99. data/db/migrate/20160718194010_add_iiif_url_to_contact.rb +6 -0
  100. data/db/migrate/20160805143841_add_upload_id_to_resources.rb +6 -0
  101. data/db/migrate/20170204091234_add_theme_to_spotlight_exhibits.rb +5 -0
  102. data/lib/generators/spotlight/install_generator.rb +5 -2
  103. data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +0 -1
  104. data/lib/generators/spotlight/templates/spotlight.scss +3 -2
  105. data/lib/migration/iiif.rb +82 -0
  106. data/lib/spotlight/engine.rb +22 -10
  107. data/lib/spotlight/version.rb +1 -1
  108. data/lib/tasks/spotlight_tasks.rake +10 -0
  109. data/spec/controllers/spotlight/about_pages_controller_spec.rb +1 -1
  110. data/spec/controllers/spotlight/appearances_controller_spec.rb +31 -18
  111. data/spec/controllers/spotlight/catalog_controller_spec.rb +40 -0
  112. data/spec/controllers/spotlight/contacts_controller_spec.rb +20 -1
  113. data/spec/controllers/spotlight/feature_pages_controller_spec.rb +2 -6
  114. data/spec/controllers/spotlight/featured_images_controller_spec.rb +74 -0
  115. data/spec/controllers/spotlight/home_pages_controller_spec.rb +1 -1
  116. data/spec/controllers/spotlight/searches_controller_spec.rb +3 -1
  117. data/spec/controllers/spotlight/sites_controller_spec.rb +6 -1
  118. data/spec/examples.txt +1118 -1059
  119. data/spec/factories/contact_images.rb +6 -0
  120. data/spec/factories/contacts.rb +4 -1
  121. data/spec/factories/exhibit_thumbnails.rb +6 -0
  122. data/spec/factories/exhibits.rb +4 -0
  123. data/spec/factories/featured_images.rb +1 -0
  124. data/spec/factories/resources.rb +2 -1
  125. data/spec/features/add_contacts_spec.rb +5 -5
  126. data/spec/features/add_iiif_manifest_spec.rb +41 -0
  127. data/spec/features/add_items_spec.rb +2 -2
  128. data/spec/features/autocomplete_typeahead_spec.rb +86 -0
  129. data/spec/features/browse_category_admin_spec.rb +27 -6
  130. data/spec/features/browse_category_spec.rb +2 -2
  131. data/spec/features/create_exhibit_spec.rb +3 -3
  132. data/spec/features/exhibit_masthead_spec.rb +20 -9
  133. data/spec/features/exhibit_themes_spec.rb +25 -0
  134. data/spec/features/home_page_spec.rb +1 -1
  135. data/spec/features/javascript/blocks/solr_documents_block_spec.rb +42 -0
  136. data/spec/features/javascript/blocks/uploaded_items_block_spec.rb +5 -3
  137. data/spec/features/javascript/feature_page_admin_spec.rb +1 -1
  138. data/spec/features/javascript/multi_image_select_spec.rb +5 -6
  139. data/spec/features/javascript/search_config_admin_spec.rb +1 -1
  140. data/spec/features/site_masthead_spec.rb +14 -4
  141. data/spec/fixtures/gk446cj2442-manifest.json +58 -0
  142. data/spec/fixtures/iiif_responses.rb +274 -0
  143. data/spec/fixtures/sample_solr_documents.yml +106 -0
  144. data/spec/helpers/spotlight/crop_helper_spec.rb +9 -0
  145. data/spec/helpers/spotlight/main_app_helpers_spec.rb +45 -0
  146. data/spec/helpers/spotlight/meta_helper_spec.rb +2 -15
  147. data/spec/lib/migration/iiif_spec.rb +70 -0
  148. data/spec/models/spotlight/blacklight_configuration_spec.rb +17 -5
  149. data/spec/models/spotlight/contact_image_spec.rb +9 -0
  150. data/spec/models/spotlight/exhibit_spec.rb +17 -20
  151. data/spec/models/spotlight/exhibit_thumbnail_spec.rb +8 -0
  152. data/spec/models/spotlight/featured_image_spec.rb +59 -10
  153. data/spec/models/spotlight/masthead_spec.rb +33 -17
  154. data/spec/models/spotlight/page_spec.rb +14 -0
  155. data/spec/models/spotlight/reindex_progress_spec.rb +22 -73
  156. data/spec/models/spotlight/resources/iiif_harvester_spec.rb +30 -0
  157. data/spec/models/spotlight/resources/iiif_manifest_spec.rb +107 -0
  158. data/spec/models/spotlight/resources/iiif_service_spec.rb +52 -0
  159. data/spec/models/spotlight/resources/upload_spec.rb +7 -3
  160. data/spec/models/spotlight/search_spec.rb +0 -45
  161. data/spec/models/spotlight/solr_document/uploaded_resource_spec.rb +11 -29
  162. data/spec/presenters/spotlight/iiif_manifest_presenter_spec.rb +123 -0
  163. data/spec/routing/spotlight/exhibit_catalog_spec.rb +4 -0
  164. data/spec/routing/spotlight/featured_images_spec.rb +21 -0
  165. data/spec/serializers/spotlight/exhibit_export_serializer_spec.rb +15 -18
  166. data/spec/services/spotlight/iiif_resource_resolver_spec.rb +90 -0
  167. data/spec/spec_helper.rb +3 -0
  168. data/spec/support/features/test_features_helpers.rb +3 -2
  169. data/spec/support/stub_iiif_response.rb +24 -0
  170. data/spec/support/views/test_view_helpers.rb +1 -0
  171. data/spec/test_app_templates/Gemfile.extra +0 -1
  172. data/spec/uploaders/spotlight/attachment_uploader_spec.rb +24 -0
  173. data/spec/uploaders/spotlight/featured_image_uploader_spec.rb +30 -0
  174. data/spec/views/_user_util_links.html.erb_spec.rb +9 -5
  175. data/spec/views/shared/_masthead.html.erb_spec.rb +5 -2
  176. data/spec/views/spotlight/browse/_search.html.erb_spec.rb +2 -2
  177. data/spec/views/spotlight/contacts/edit.html.erb_spec.rb +4 -7
  178. data/spec/views/spotlight/metadata_configurations/_metadata_field.html.erb_spec.rb +7 -2
  179. data/spec/views/spotlight/pages/edit.html.erb_spec.rb +3 -1
  180. data/spec/views/spotlight/pages/new.html.erb_spec.rb +3 -1
  181. data/spec/views/spotlight/searches/_search.html.erb_spec.rb +3 -2
  182. data/spec/views/spotlight/searches/edit.html.erb_spec.rb +3 -2
  183. data/vendor/assets/javascripts/leaflet-areaselect.js +184 -0
  184. data/vendor/assets/javascripts/leaflet-iiif.js +230 -0
  185. data/vendor/assets/javascripts/leaflet.js +9 -0
  186. data/vendor/assets/javascripts/polyfill.min.js +4 -0
  187. data/vendor/assets/stylesheets/leaflet-areaselect.css +15 -0
  188. data/vendor/assets/stylesheets/leaflet.css +624 -0
  189. metadata +136 -62
  190. data/app/assets/javascripts/spotlight/jcrop.js +0 -1696
  191. data/app/helpers/spotlight/jcrop_helper.rb +0 -37
  192. data/app/models/concerns/spotlight/default_thumbnailable.rb +0 -25
  193. data/app/models/concerns/spotlight/image_derivatives.rb +0 -58
  194. data/app/models/concerns/spotlight/solr_document/spotlight_images.rb +0 -55
  195. data/app/uploaders/spotlight/avatar_uploader.rb +0 -24
  196. data/app/uploaders/spotlight/item_uploader.rb +0 -25
  197. data/app/uploaders/spotlight/masthead_uploader.rb +0 -22
  198. data/spec/helpers/spotlight/jcrop_helper_spec.rb +0 -33
  199. data/spec/models/spotlight/default_thumbnailable_concern_spec.rb +0 -16
  200. data/spec/models/spotlight/image_derivatives_spec.rb +0 -15
  201. data/spec/models/spotlight/solr_document/spotlight_images_spec.rb +0 -42
  202. data/spec/uploaders/spotlight/item_uploader_spec.rb +0 -67
@@ -0,0 +1,30 @@
1
+ describe Spotlight::FeaturedImageUploader do
2
+ let(:mounter) { FactoryGirl.create(:featured_image) }
3
+ subject(:featured_image_uploader) { described_class.new(mounter, 'mounted_as') }
4
+
5
+ describe '#extension_white_list' do
6
+ it 'is the configured array of approved extension to be uploaded' do
7
+ expect(featured_image_uploader.extension_white_list).to eq Spotlight::Engine.config.allowed_upload_extensions
8
+ end
9
+ end
10
+
11
+ describe '#store_dir' do
12
+ let(:store_dir) { featured_image_uploader.store_dir }
13
+
14
+ it 'is prefixed with "uploads/spotlight"' do
15
+ expect(store_dir).to start_with 'uploads/spotlight/'
16
+ end
17
+
18
+ it "includes the mounter's class name" do
19
+ expect(store_dir).to match '/featured_image/'
20
+ end
21
+
22
+ it 'includes the mounted_as option' do
23
+ expect(store_dir).to match '/mounted_as/'
24
+ end
25
+
26
+ it "ends with the mounter's id" do
27
+ expect(store_dir).to end_with "/#{mounter.id}"
28
+ end
29
+ end
30
+ end
@@ -32,7 +32,7 @@ describe '_user_util_links', type: :view do
32
32
  it 'renders the links' do
33
33
  render
34
34
  expect(rendered).to have_link 'Feedback'
35
- expect(rendered).to_not have_link 'Dashboard'
35
+ expect(rendered).to_not have_link 'Exhibit dashboard'
36
36
  expect(rendered).to have_link 'Sign out'
37
37
  end
38
38
  end
@@ -40,6 +40,7 @@ describe '_user_util_links', type: :view do
40
40
  describe 'when user is a curator' do
41
41
  let(:current_user) { Spotlight::Engine.user_class.new }
42
42
  before do
43
+ allow(view).to receive(:can?).with(:manage, Spotlight::Site).and_return(false)
43
44
  allow(view).to receive(:can?).with(:update, current_exhibit).and_return(false)
44
45
  allow(view).to receive(:can?).with(:create, Spotlight::Exhibit).and_return(false)
45
46
  allow(view).to receive(:can?).with(:curate, current_exhibit).and_return(true)
@@ -47,7 +48,7 @@ describe '_user_util_links', type: :view do
47
48
  it 'renders the links' do
48
49
  render
49
50
  expect(rendered).to have_link 'Feedback'
50
- expect(rendered).to have_link 'Dashboard'
51
+ expect(rendered).to have_link 'Exhibit dashboard'
51
52
  expect(rendered).to have_link 'Sign out'
52
53
  end
53
54
  end
@@ -55,6 +56,7 @@ describe '_user_util_links', type: :view do
55
56
  describe 'when user is an admin' do
56
57
  let(:current_user) { Spotlight::Engine.user_class.new }
57
58
  before do
59
+ allow(view).to receive(:can?).with(:manage, Spotlight::Site).and_return(false)
58
60
  allow(view).to receive(:can?).with(:update, current_exhibit).and_return(true)
59
61
  allow(view).to receive(:can?).with(:create, Spotlight::Exhibit).and_return(false)
60
62
  allow(view).to receive(:can?).with(:curate, current_exhibit).and_return(true)
@@ -62,7 +64,7 @@ describe '_user_util_links', type: :view do
62
64
  it 'renders the links' do
63
65
  render
64
66
  expect(rendered).to have_link 'Feedback'
65
- expect(rendered).to have_link 'Dashboard'
67
+ expect(rendered).to have_link 'Exhibit dashboard'
66
68
  expect(rendered).to have_link 'Sign out'
67
69
  end
68
70
  end
@@ -70,6 +72,7 @@ describe '_user_util_links', type: :view do
70
72
  describe 'when user is a site-wide admin' do
71
73
  let(:current_user) { Spotlight::Engine.user_class.new }
72
74
  before do
75
+ allow(view).to receive(:can?).with(:manage, Spotlight::Site).and_return(true)
73
76
  allow(view).to receive(:can?).with(:update, current_exhibit).and_return(true)
74
77
  allow(view).to receive(:can?).with(:create, Spotlight::Exhibit).and_return(true)
75
78
  allow(view).to receive(:can?).with(:curate, current_exhibit).and_return(true)
@@ -77,8 +80,9 @@ describe '_user_util_links', type: :view do
77
80
  it 'renders the links' do
78
81
  render
79
82
  expect(rendered).to have_link 'Feedback'
80
- expect(rendered).to have_link 'Dashboard'
81
- expect(rendered).to have_link 'Create Exhibit'
83
+ expect(rendered).to have_link 'Site administration'
84
+ expect(rendered).to have_link 'Exhibit dashboard'
85
+ expect(rendered).to have_link 'Create new exhibit'
82
86
  expect(rendered).to have_link 'Sign out'
83
87
  end
84
88
  end
@@ -31,7 +31,10 @@ describe 'shared/_masthead', type: :view do
31
31
  end
32
32
 
33
33
  context 'with an exhibit masthead' do
34
- let(:masthead) { FactoryGirl.create(:masthead) }
34
+ let(:iiif_url) { 'http://test.host/1/10,10,130,1210/1800,180/0/default.jpg' }
35
+ let(:masthead) do
36
+ FactoryGirl.create(:masthead, iiif_tilesource: 'http://test.host/1', iiif_region: '10,10,130,1210')
37
+ end
35
38
 
36
39
  before do
37
40
  exhibit.masthead = masthead
@@ -47,7 +50,7 @@ describe 'shared/_masthead', type: :view do
47
50
  render
48
51
  expect(rendered).to have_selector '.background-container'
49
52
  expect(rendered).to have_selector '.background-container-gradient'
50
- expect(rendered).to match(/background-image: url\('#{masthead.image.cropped.url}'\)/)
53
+ expect(rendered).to match(/background-image: url\('#{iiif_url}'\)/)
51
54
  end
52
55
  end
53
56
 
@@ -4,7 +4,7 @@ describe 'spotlight/browse/search', type: :view do
4
4
  let(:exhibit) { FactoryGirl.create(:exhibit) }
5
5
  before do
6
6
  allow(search).to receive_messages(documents: double(size: 15))
7
- allow(search).to receive_message_chain(:thumbnail, :image, thumb: '/some/image')
7
+ allow(search).to receive_message_chain(:thumbnail, iiif_url: '/some/image')
8
8
  end
9
9
 
10
10
  before do
@@ -13,7 +13,7 @@ describe 'spotlight/browse/search', type: :view do
13
13
 
14
14
  it 'displays the image' do
15
15
  render partial: 'spotlight/browse/search', locals: { search: search }
16
- expect(response).to have_selector 'a img'
16
+ expect(response).to have_selector 'a img[src="/some/image"]'
17
17
  end
18
18
 
19
19
  it 'has a heading' do
@@ -9,18 +9,15 @@ describe 'spotlight/contacts/edit.html.erb' do
9
9
  before do
10
10
  allow(view).to receive(:exhibit_contacts_path).and_return('/exhibit/1/contacts')
11
11
  allow(view).to receive(:exhibit_about_pages_path).and_return('/exhibit/admin/about')
12
+ allow(view).to receive(:contact_images_path).and_return('/contact_images')
12
13
  assign(:contact, contact)
13
14
  assign(:exhibit, exhibit)
14
15
  allow(view).to receive(:current_exhibit).and_return(exhibit)
15
16
  end
16
17
 
17
- it 'has a photo field' do
18
+ it 'has an IIIF crop' do
18
19
  render
19
- expect(rendered).to have_content 'Photo'
20
- end
21
-
22
- it 'has a cropbox' do
23
- render
24
- expect(rendered).to have_selector '#contact_avatar_cropbox'
20
+ expect(rendered).to have_content 'Upload an image'
21
+ expect(rendered).to have_selector '#contact_avatar_attributes_iiif_cropper'
25
22
  end
26
23
  end
@@ -12,12 +12,17 @@ describe 'spotlight/metadata_configurations/_metadata_field', type: :view do
12
12
  )
13
13
  end
14
14
 
15
- let(:facet_field) { Blacklight::Configuration::FacetField.new }
15
+ let(:field) { Blacklight::Configuration::Field.new immutable: OpenStruct.new(another_view_type: false) }
16
16
  let(:builder) { ActionView::Helpers::FormBuilder.new 'z', nil, view, {} }
17
17
 
18
18
  it 'uses the index_field_label helper to render the label' do
19
19
  allow(view).to receive(:index_field_label).with(nil, 'some_key').and_return 'Some label'
20
- render partial: p, locals: { key: 'some_key', config: facet_field, f: builder }
20
+ render partial: p, locals: { key: 'some_key', config: field, f: builder }
21
21
  expect(rendered).to have_selector '.field-label', text: 'Some label'
22
22
  end
23
+
24
+ it 'marks views as disabled if they are immutable' do
25
+ render partial: p, locals: { key: 'some_key', config: field, f: builder }
26
+ expect(rendered).to have_selector 'input[disabled][name="z[some_key][another_view_type]"]'
27
+ end
23
28
  end
@@ -3,7 +3,9 @@ describe 'spotlight/pages/edit', type: :view do
3
3
  let(:page) { stub_model(Spotlight::FeaturePage, exhibit: exhibit) }
4
4
  before do
5
5
  assign(:page, page)
6
- allow(view).to receive_messages(default_thumbnail_jcrop_options: {}, available_index_fields: [], available_view_fields: [])
6
+ allow(view).to receive_messages(featured_images_path: '/foo',
7
+ available_index_fields: [],
8
+ available_view_fields: [])
7
9
  end
8
10
 
9
11
  it 'renders the edit page form' do
@@ -2,7 +2,9 @@ describe 'spotlight/pages/new', type: :view do
2
2
  let(:exhibit) { stub_model(Spotlight::Exhibit) }
3
3
  before do
4
4
  assign(:page, stub_model(Spotlight::FeaturePage, exhibit: exhibit).as_new_record)
5
- allow(view).to receive_messages(default_thumbnail_jcrop_options: {}, available_index_fields: [], available_view_fields: [])
5
+ allow(view).to receive_messages(featured_images_path: '/foo',
6
+ available_index_fields: [],
7
+ available_view_fields: [])
6
8
  end
7
9
 
8
10
  it 'renders new page form' do
@@ -10,11 +10,12 @@ describe 'spotlight/searches/_search.html.erb', type: :view do
10
10
  }
11
11
  })
12
12
  end
13
+ let(:thumbnail) { double(id: '7777', iiif_url: '/images/7777/full/400,300/0/default.jpg') }
13
14
 
14
15
  before do
15
16
  allow(view).to receive(:edit_search_path).and_return('/edit')
16
17
  allow(view).to receive(:search_path).and_return('/search')
17
- allow(search).to receive_message_chain(:thumbnail, :image, thumb: '/some/image')
18
+ allow(search).to receive(:thumbnail).and_return(thumbnail)
18
19
  allow(search).to receive(:count).and_return(15)
19
20
  allow(search).to receive(:params).and_return({})
20
21
 
@@ -29,7 +30,7 @@ describe 'spotlight/searches/_search.html.erb', type: :view do
29
30
  # rubocop:enable RSpec/InstanceVariable
30
31
  expect(rendered).to have_selector "li[data-id='99']"
31
32
  expect(rendered).to have_selector '.panel-heading .main .title', text: 'Title1'
32
- expect(rendered).to have_selector 'img[src="/some/image"]'
33
+ expect(rendered).to have_selector 'img[src="/images/7777/full/400,300/0/default.jpg"]'
33
34
  expect(rendered).to have_selector 'input[type=hidden][data-property=weight]', visible: false
34
35
  end
35
36
  end
@@ -1,4 +1,3 @@
1
-
2
1
  describe 'spotlight/searches/edit.html.erb', type: :view do
3
2
  let(:blacklight_config) do
4
3
  Blacklight::Configuration.new do |config|
@@ -10,15 +9,17 @@ describe 'spotlight/searches/edit.html.erb', type: :view do
10
9
  stub_model(Spotlight::Search, exhibit: exhibit,
11
10
  id: 99, title: 'Title1', query_params: { f: { 'some_field' => ['xyz'] } })
12
11
  end
12
+
13
13
  before do
14
14
  allow(view).to receive(:search_action_path).and_return('/search')
15
+ allow(view).to receive(:mastheads_path).and_return('/mastheads')
16
+ allow(view).to receive(:featured_images_path).and_return('/featured_images')
15
17
  allow(view).to receive(:exhibit_search_path).and_return('/search')
16
18
  allow(view).to receive(:exhibit_searches_path).and_return('/searches')
17
19
  allow(view).to receive(:blacklight_config).and_return(blacklight_config)
18
20
  assign(:exhibit, exhibit)
19
21
  assign(:search, search)
20
22
  allow(view).to receive(:current_exhibit).and_return(exhibit)
21
- allow(view).to receive_messages(default_masthead_jcrop_options: {}, default_thumbnail_jcrop_options: {})
22
23
  end
23
24
 
24
25
  it 'renders a form w/ the appropriate autocomplete data attribute' do
@@ -0,0 +1,184 @@
1
+ L.AreaSelect = L.Class.extend({
2
+ includes: L.Mixin.Events,
3
+
4
+ options: {
5
+ width: 200,
6
+ height: 300,
7
+ keepAspectRatio: false,
8
+ },
9
+
10
+ initialize: function(options) {
11
+ L.Util.setOptions(this, options);
12
+
13
+ this._width = this.options.width;
14
+ this._height = this.options.height;
15
+ },
16
+
17
+ addTo: function(map) {
18
+ this.map = map;
19
+ this._createElements();
20
+ this._render();
21
+ return this;
22
+ },
23
+
24
+ getBounds: function() {
25
+ var size = this.map.getSize();
26
+ var topRight = new L.Point();
27
+ var bottomLeft = new L.Point();
28
+
29
+ bottomLeft.x = Math.round((size.x - this._width) / 2);
30
+ topRight.y = Math.round((size.y - this._height) / 2);
31
+ topRight.x = size.x - bottomLeft.x;
32
+ bottomLeft.y = size.y - topRight.y;
33
+
34
+ var sw = this.map.containerPointToLatLng(bottomLeft);
35
+ var ne = this.map.containerPointToLatLng(topRight);
36
+
37
+ return new L.LatLngBounds(sw, ne);
38
+ },
39
+
40
+ remove: function() {
41
+ this.map.off("moveend", this._onMapChange);
42
+ this.map.off("zoomend", this._onMapChange);
43
+ this.map.off("resize", this._onMapResize);
44
+
45
+ this._container.parentNode.removeChild(this._container);
46
+ },
47
+
48
+
49
+ setDimensions: function(dimensions) {
50
+ if (!dimensions)
51
+ return;
52
+
53
+ this._height = parseInt(dimensions.height) || this._height;
54
+ this._width = parseInt(dimensions.width) || this._width;
55
+ this._render();
56
+ this.fire("change");
57
+ },
58
+
59
+
60
+ _createElements: function() {
61
+ if (!!this._container)
62
+ return;
63
+
64
+ this._container = L.DomUtil.create("div", "leaflet-areaselect-container", this.map._controlContainer)
65
+ this._topShade = L.DomUtil.create("div", "leaflet-areaselect-shade leaflet-control", this._container);
66
+ this._bottomShade = L.DomUtil.create("div", "leaflet-areaselect-shade leaflet-control", this._container);
67
+ this._leftShade = L.DomUtil.create("div", "leaflet-areaselect-shade leaflet-control", this._container);
68
+ this._rightShade = L.DomUtil.create("div", "leaflet-areaselect-shade leaflet-control", this._container);
69
+
70
+ this._nwHandle = L.DomUtil.create("div", "leaflet-areaselect-handle leaflet-control", this._container);
71
+ this._swHandle = L.DomUtil.create("div", "leaflet-areaselect-handle leaflet-control", this._container);
72
+ this._neHandle = L.DomUtil.create("div", "leaflet-areaselect-handle leaflet-control", this._container);
73
+ this._seHandle = L.DomUtil.create("div", "leaflet-areaselect-handle leaflet-control", this._container);
74
+
75
+ this._setUpHandlerEvents(this._nwHandle);
76
+ this._setUpHandlerEvents(this._neHandle, -1, 1);
77
+ this._setUpHandlerEvents(this._swHandle, 1, -1);
78
+ this._setUpHandlerEvents(this._seHandle, -1, -1);
79
+
80
+ this.map.on("moveend", this._onMapChange, this);
81
+ this.map.on("zoomend", this._onMapChange, this);
82
+ this.map.on("resize", this._onMapResize, this);
83
+
84
+ this.fire("change");
85
+ },
86
+
87
+ _setUpHandlerEvents: function(handle, xMod, yMod) {
88
+ xMod = xMod || 1;
89
+ yMod = yMod || 1;
90
+
91
+ var self = this;
92
+ function onMouseDown(event) {
93
+ event.stopPropagation();
94
+ self.map.dragging.disable();
95
+ L.DomEvent.removeListener(this, "mousedown", onMouseDown);
96
+ var curX = event.pageX;
97
+ var curY = event.pageY;
98
+ var ratio = self._width / self._height;
99
+ var size = self.map.getSize();
100
+
101
+ function onMouseMove(event) {
102
+ if (self.options.keepAspectRatio) {
103
+ var maxHeight = (self._height >= self._width ? size.y : (size.x / ratio) ) - 30;
104
+ self._height += (curY - event.originalEvent.pageY) * 2 * yMod;
105
+ self._height = Math.max(30, self._height);
106
+ self._height = Math.min(maxHeight, self._height);
107
+ self._width = self._height * ratio;
108
+ } else {
109
+ self._width += (curX - event.originalEvent.pageX) * 2 * xMod;
110
+ self._height += (curY - event.originalEvent.pageY) * 2 * yMod;
111
+ self._width = Math.max(30, self._width);
112
+ self._height = Math.max(30, self._height);
113
+ self._width = Math.min(size.x-30, self._width);
114
+ self._height = Math.min(size.y-30, self._height);
115
+
116
+ }
117
+
118
+ curX = event.originalEvent.pageX;
119
+ curY = event.originalEvent.pageY;
120
+ self._render();
121
+ }
122
+ function onMouseUp(event) {
123
+ self.map.dragging.enable();
124
+ L.DomEvent.removeListener(self.map, "mouseup", onMouseUp);
125
+ L.DomEvent.removeListener(self.map, "mousemove", onMouseMove);
126
+ L.DomEvent.addListener(handle, "mousedown", onMouseDown);
127
+ self.fire("change");
128
+ }
129
+
130
+ L.DomEvent.addListener(self.map, "mousemove", onMouseMove);
131
+ L.DomEvent.addListener(self.map, "mouseup", onMouseUp);
132
+ }
133
+ L.DomEvent.addListener(handle, "mousedown", onMouseDown);
134
+ },
135
+
136
+ _onMapResize: function() {
137
+ this._render();
138
+ },
139
+
140
+ _onMapChange: function() {
141
+ this.fire("change");
142
+ },
143
+
144
+ _render: function() {
145
+ var size = this.map.getSize();
146
+ var handleOffset = Math.round(this._nwHandle.offsetWidth/2);
147
+
148
+ var topBottomHeight = Math.round((size.y-this._height)/2);
149
+ var leftRightWidth = Math.round((size.x-this._width)/2);
150
+
151
+ function setDimensions(element, dimension) {
152
+ element.style.width = dimension.width + "px";
153
+ element.style.height = dimension.height + "px";
154
+ element.style.top = dimension.top + "px";
155
+ element.style.left = dimension.left + "px";
156
+ element.style.bottom = dimension.bottom + "px";
157
+ element.style.right = dimension.right + "px";
158
+ }
159
+
160
+ setDimensions(this._topShade, {width:size.x, height:topBottomHeight, top:0, left:0});
161
+ setDimensions(this._bottomShade, {width:size.x, height:topBottomHeight, bottom:0, left:0});
162
+ setDimensions(this._leftShade, {
163
+ width: leftRightWidth,
164
+ height: size.y-(topBottomHeight*2),
165
+ top: topBottomHeight,
166
+ left: 0
167
+ });
168
+ setDimensions(this._rightShade, {
169
+ width: leftRightWidth,
170
+ height: size.y-(topBottomHeight*2),
171
+ top: topBottomHeight,
172
+ right: 0
173
+ });
174
+
175
+ setDimensions(this._nwHandle, {left:leftRightWidth-handleOffset, top:topBottomHeight-7});
176
+ setDimensions(this._neHandle, {right:leftRightWidth-handleOffset, top:topBottomHeight-7});
177
+ setDimensions(this._swHandle, {left:leftRightWidth-handleOffset, bottom:topBottomHeight-7});
178
+ setDimensions(this._seHandle, {right:leftRightWidth-handleOffset, bottom:topBottomHeight-7});
179
+ }
180
+ });
181
+
182
+ L.areaSelect = function(options) {
183
+ return new L.AreaSelect(options);
184
+ }
@@ -0,0 +1,230 @@
1
+ /*
2
+ * Leaflet-IIIF 1.0.2
3
+ * IIIF Viewer for Leaflet
4
+ * by Jack Reed, @mejackreed
5
+ */
6
+
7
+ L.TileLayer.Iiif = L.TileLayer.extend({
8
+ options: {
9
+ continuousWorld: true,
10
+ tileSize: 256,
11
+ updateWhenIdle: true,
12
+ tileFormat: 'jpg',
13
+ fitBounds: true
14
+ },
15
+
16
+ initialize: function(url, options) {
17
+ options = typeof options !== 'undefined' ? options : {};
18
+
19
+ if (options.maxZoom) {
20
+ this._customMaxZoom = true;
21
+ }
22
+
23
+ // Check for explicit tileSize set
24
+ if (options.tileSize) {
25
+ this._explicitTileSize = true;
26
+ }
27
+
28
+ options = L.setOptions(this, options);
29
+ this._infoDeferred = new $.Deferred();
30
+ this._infoUrl = url;
31
+ this._baseUrl = this._templateUrl();
32
+ this._getInfo();
33
+ },
34
+ getTileUrl: function(coords) {
35
+ var _this = this,
36
+ x = coords.x,
37
+ y = (coords.y),
38
+ zoom = _this._getZoomForUrl(),
39
+ scale = Math.pow(2, _this.maxNativeZoom - zoom),
40
+ tileBaseSize = _this.options.tileSize * scale,
41
+ minx = (x * tileBaseSize),
42
+ miny = (y * tileBaseSize),
43
+ maxx = Math.min(minx + tileBaseSize, _this.x),
44
+ maxy = Math.min(miny + tileBaseSize, _this.y);
45
+
46
+ var xDiff = (maxx - minx);
47
+ var yDiff = (maxy - miny);
48
+
49
+ return L.Util.template(this._baseUrl, L.extend({
50
+ format: _this.options.tileFormat,
51
+ quality: _this.quality,
52
+ region: [minx, miny, xDiff, yDiff].join(','),
53
+ rotation: 0,
54
+ size: Math.ceil(xDiff / scale) + ','
55
+ }, this.options));
56
+ },
57
+ onAdd: function(map) {
58
+ var _this = this;
59
+
60
+ // Wait for deferred to complete
61
+ $.when(_this._infoDeferred).done(function() {
62
+
63
+ // Set maxZoom for map
64
+ map._layersMaxZoom = _this.maxZoom;
65
+
66
+ // Call add TileLayer
67
+ L.TileLayer.prototype.onAdd.call(_this, map);
68
+
69
+ if (_this.options.fitBounds) {
70
+ _this._fitBounds();
71
+ }
72
+
73
+ // Reset tile sizes to handle non 256x256 IIIF tiles
74
+ _this.on('tileload', function(tile, url) {
75
+
76
+ var height = tile.tile.naturalHeight,
77
+ width = tile.tile.naturalWidth;
78
+
79
+ // No need to resize if tile is 256 x 256
80
+ if (height === 256 && width === 256) return;
81
+
82
+ tile.tile.style.width = width + 'px';
83
+ tile.tile.style.height = height + 'px';
84
+
85
+ });
86
+ });
87
+ },
88
+ _fitBounds: function() {
89
+ var _this = this;
90
+
91
+ // Find best zoom level and center map
92
+ var initialZoom = _this._getInitialZoom(_this._map.getSize());
93
+ var imageSize = _this._imageSizes[initialZoom];
94
+ var sw = _this._map.options.crs.pointToLatLng(L.point(0, imageSize.y), initialZoom);
95
+ var ne = _this._map.options.crs.pointToLatLng(L.point(imageSize.x, 0), initialZoom);
96
+ var bounds = L.latLngBounds(sw, ne);
97
+
98
+ _this._map.fitBounds(bounds, true);
99
+ },
100
+ _getInfo: function() {
101
+ var _this = this;
102
+
103
+ // Look for a way to do this without jQuery
104
+ $.getJSON(_this._infoUrl)
105
+ .done(function(data) {
106
+ _this.y = data.height;
107
+ _this.x = data.width;
108
+
109
+ var tierSizes = [],
110
+ imageSizes = [],
111
+ scale,
112
+ width_,
113
+ height_,
114
+ tilesX_,
115
+ tilesY_;
116
+
117
+ // Set quality based off of IIIF version
118
+ if (data.profile instanceof Array) {
119
+ _this.profile = data.profile[0];
120
+ }else {
121
+ _this.profile = data.profile;
122
+ }
123
+
124
+ _this._setQuality();
125
+
126
+ // Unless an explicit tileSize is set, use a preferred tileSize
127
+ if (!_this._explicitTileSize) {
128
+ // Set the default first
129
+ _this.options.tileSize = 256;
130
+ if (data.tiles) {
131
+ // Image API 2.0 Case
132
+ _this.options.tileSize = data.tiles[0].width;
133
+ } else if (data.tile_width){
134
+ // Image API 1.1 Case
135
+ _this.options.tileSize = data.tile_width;
136
+ }
137
+ }
138
+
139
+ ceilLog2 = function(x) {
140
+ return Math.ceil(Math.log(x) / Math.LN2);
141
+ };
142
+
143
+ // Calculates maximum native zoom for the layer
144
+ _this.maxNativeZoom = Math.max(ceilLog2(_this.x / _this.options.tileSize),
145
+ ceilLog2(_this.y / _this.options.tileSize));
146
+
147
+ // Enable zooming further than native if maxZoom option supplied
148
+ if (_this._customMaxZoom && _this.options.maxZoom > _this.maxNativeZoom) {
149
+ _this.maxZoom = _this.options.maxZoom;
150
+ }
151
+ else {
152
+ _this.maxZoom = _this.maxNativeZoom;
153
+ }
154
+
155
+ for (var i = 0; i <= _this.maxZoom; i++) {
156
+ scale = Math.pow(2, _this.maxNativeZoom - i);
157
+ width_ = Math.ceil(_this.x / scale);
158
+ height_ = Math.ceil(_this.y / scale);
159
+ tilesX_ = Math.ceil(width_ / _this.options.tileSize);
160
+ tilesY_ = Math.ceil(height_ / _this.options.tileSize);
161
+ tierSizes.push([tilesX_, tilesY_]);
162
+ imageSizes.push(L.point(width_,height_));
163
+ }
164
+
165
+ _this._tierSizes = tierSizes;
166
+ _this._imageSizes = imageSizes;
167
+
168
+ // Resolved Deferred to initiate tilelayer load
169
+ _this._infoDeferred.resolve();
170
+ });
171
+ },
172
+
173
+ _setQuality: function() {
174
+ var _this = this;
175
+
176
+ // Quality already specified by consumer
177
+ if (_this.options.quality) {
178
+ return;
179
+ }
180
+
181
+ // Set the quality based on the IIIF compliance level
182
+ switch (true) {
183
+ case /^http:\/\/library.stanford.edu\/iiif\/image-api\/1.1\/compliance.html.*$/.test(_this.profile):
184
+ _this.options.quality = 'native';
185
+ break;
186
+ case /^http:\/\/iiif.io\/api\/image\/2.*$/.test(_this.profile):
187
+ _this.options.quality = 'default';
188
+ break;
189
+ }
190
+ },
191
+
192
+ _infoToBaseUrl: function() {
193
+ return this._infoUrl.replace('info.json', '');
194
+ },
195
+ _templateUrl: function() {
196
+ return this._infoToBaseUrl() + '{region}/{size}/{rotation}/{quality}.{format}';
197
+ },
198
+ _isValidTile: function(coords) {
199
+ var _this = this,
200
+ zoom = _this._getZoomForUrl(),
201
+ sizes = _this._tierSizes[zoom],
202
+ x = coords.x,
203
+ y = (coords.y);
204
+
205
+ if (!sizes) return false;
206
+ if (x < 0 || sizes[0] <= x || y < 0 || sizes[1] <= y) {
207
+ return false;
208
+ }else {
209
+ return true;
210
+ }
211
+ },
212
+ _getInitialZoom: function (mapSize) {
213
+ var _this = this,
214
+ tolerance = 0.8,
215
+ imageSize;
216
+
217
+ for (var i = _this.maxNativeZoom; i >= 0; i--) {
218
+ imageSize = this._imageSizes[i];
219
+ if (imageSize.x * tolerance < mapSize.x && imageSize.y * tolerance < mapSize.y) {
220
+ return i;
221
+ }
222
+ }
223
+ // return a default zoom
224
+ return 2;
225
+ }
226
+ });
227
+
228
+ L.tileLayer.iiif = function(url, options) {
229
+ return new L.TileLayer.Iiif(url, options);
230
+ };