blacklight_browse_nearby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/LICENSE +14 -0
  2. data/README.rdoc +118 -0
  3. data/Rakefile +23 -0
  4. data/SOLR_README.rdoc +144 -0
  5. data/app/assets/javascripts/blacklight_browse_nearby/blacklight_browse_nearby.js +12 -0
  6. data/app/assets/stylesheets/blacklight_browse_nearby/blacklight_browse_nearby.css.scss +11 -0
  7. data/app/controllers/blacklight_browse_nearby_controller.rb +25 -0
  8. data/app/helpers/blacklight_browse_nearby_helper.rb +7 -0
  9. data/app/views/blacklight_browse_nearby/_browse_view_pagination.html.erb +6 -0
  10. data/app/views/blacklight_browse_nearby/_nearby_controls.html.erb +11 -0
  11. data/app/views/blacklight_browse_nearby/_nearby_item.html.erb +8 -0
  12. data/app/views/blacklight_browse_nearby/_nearby_items.html.erb +22 -0
  13. data/app/views/blacklight_browse_nearby/index.html.erb +3 -0
  14. data/app/views/blacklight_browse_nearby/index.js.erb +2 -0
  15. data/app/views/catalog/_document_header.html.erb +15 -0
  16. data/config/locales/blacklight_browse_nearby.en.yml +11 -0
  17. data/config/routes.rb +3 -0
  18. data/lib/blacklight_browse_nearby.rb +156 -0
  19. data/lib/blacklight_browse_nearby/controller.rb +16 -0
  20. data/lib/blacklight_browse_nearby/engine.rb +4 -0
  21. data/lib/blacklight_browse_nearby/version.rb +3 -0
  22. data/lib/blacklight_browse_nearby_config.rb +11 -0
  23. data/lib/generators/blacklight_browse_nearby_generator.rb +42 -0
  24. data/lib/tasks/blacklight_browse_nearby_tasks.rake +4 -0
  25. data/spec/acceptance/blacklight_browse_nearby_spec.rb +283 -0
  26. data/spec/internal/app/controllers/application_controller.rb +3 -0
  27. data/spec/internal/app/models/solr_document.rb +3 -0
  28. data/spec/internal/config/database.yml +3 -0
  29. data/spec/internal/config/routes.rb +6 -0
  30. data/spec/internal/config/solr.yml +6 -0
  31. data/spec/internal/db/combustion_test.sqlite +0 -0
  32. data/spec/internal/db/schema.rb +3 -0
  33. data/spec/internal/log/test.log +289 -0
  34. data/spec/internal/public/favicon.ico +0 -0
  35. data/spec/spec_helper.rb +19 -0
  36. metadata +209 -0
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ #############################################################################################################
2
+ # Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
@@ -0,0 +1,118 @@
1
+ = Blacklight Browse Nearby
2
+
3
+ This is a gem that will provide a small bit of HTML to provide on a record's show view as well as a larger browse view which will render the appropriate document partials as a search result.
4
+
5
+ This gem has several configuration options that will need to match your solr configuration and some very specific solr field formatting necessary to work properly. Continue reading below to get more information on how to properly configure both solr and the application and how to format these special fields.
6
+
7
+ == Installation
8
+
9
+ $ gem install blacklight_browse_nearby
10
+ $ rails g blacklight_browse_nearby
11
+
12
+ In order to display the small browse nearby UI you must insert the render_nearby_items helper method into a view. This view must be under the show action of a controller that has included the BlacklightBrowseNearby::Controller (CatalogController by default).
13
+
14
+ <%= render_nearby_items %>
15
+
16
+
17
+ == Basic Usage
18
+
19
+ Simply giving an object ID will return the configured number of documents before and after (including the center document) the given ID (555).
20
+
21
+ BlacklightBrowseNearby.new("555").documents
22
+
23
+ Passing a number (without a page) will return the requested number of documents before and after (including the center document) the given ID (555).
24
+
25
+ BlacklightBrowseNearby.new("555", :number => 11).documents
26
+
27
+ Passing a page will return the configured number of documents before or after the given ID (555). Negative page numbers return documents before and positive numbers return documents after. (the number parameter will work as expected)
28
+
29
+ BlacklightBrowseNearby.new("555", :page => -3).documents # documents before
30
+ BlacklightBrowseNearby.new("555", :page => 3).documents # documents after
31
+
32
+
33
+ == Configurations
34
+
35
+ The solr field name of the human readable value that we're sorting on. (default: "value_display")
36
+
37
+ BlacklightBrowseNearby::Engine.config.value_field
38
+
39
+ The solr field name of the sortable values that will be used by the solr terms component. (default: "shelfkey")
40
+
41
+ BlacklightBrowseNearby::Engine.config.sortkey_field
42
+
43
+ The solr field name of the reverse sortable values that will be used by the solr terms component. (default: "reverse_shelfkey")
44
+
45
+ BlacklightBrowseNearby::Engine.config.reverse_sortkey_field
46
+
47
+ The solr field name of the combined keys that will be used to handle switching between values in multi-valued fields. (default: "combined_shelfkey")
48
+
49
+ BlacklightBrowseNearby::Engine.config.combined_key_field
50
+
51
+ The string used to delimit values in the combined key field. (default: "-|-")
52
+
53
+ BlacklightBrowseNearby::Engine.config.key_delimiter
54
+
55
+ The pattern of combined key field. It is not recommended that you change this unless you absolutely have to. You should be able to cover most use cases by changing other configuration options.
56
+
57
+ BlacklightBrowseNearby::Engine.config.combined_key_pattern
58
+
59
+ The request handler for the for the solr terms component. (default: "/alphaTerms")
60
+
61
+ BlacklightBrowseNearby::Engine.config.request_handler
62
+
63
+ The default number of records to retrieve in the smaller nearby view. This should be an odd number because we want to get an even number before and after the center document. (default: "5")
64
+
65
+ BlacklightBrowseNearby::Engine.config.default_hits
66
+
67
+ The default number of records to retrieve in the larger browse view. This should be an odd number because we want to get an even number before and after the center document. (default: "11")
68
+
69
+ BlacklightBrowseNearby::Engine.config.full_view_default_hits
70
+
71
+ The fields that the default nearby_item partial will attempt to display. You will most likely want to override the nearby_item partial. (default: The value of BlacklightBrowseNearby::Engine.config.value_field)
72
+
73
+ BlacklightBrowseNearby::Engine.config.nearby_fields
74
+
75
+ The field that the default nearby_item partial will attempt to use as a title and link. You will most likely want to override the nearby_item partial. (default: "title_display")
76
+
77
+ BlacklightBrowseNearby::Engine.config.link_field
78
+
79
+
80
+ You can set these configuration options from somewhere in your application (e.g. an initializer)
81
+
82
+ == Necessary Fields in Solr
83
+
84
+ There are 4 configurable solr fields necessary for this gem to function properly. value_field, sortkey_field, reverse_sortkey_field, and combined_key_field. These should all be multi-valued fields.
85
+
86
+ === Combined Key
87
+
88
+ The combined key field is necessary to handle documents with multiple values to browse from (e.g. multiple callnumbers). This key should consist of a combination of the 3 other necessary fields in solr delimited by a special configurable string.
89
+
90
+ A combined key field that matched the default configuration would look something like:
91
+
92
+ ["AAAA -|- aaaa -|- zzzz", "BBBB -|- bbbb -|- yyyy"]
93
+
94
+ This gem will be able to handle the pattern change when you update the configured solr field names or delimiter. However; if you drastically change the pattern of this field you will need to update the combined_key_pattern configuration to match the new pattern.
95
+
96
+ === More information about sort keys and solr configurations
97
+
98
+ For a more detailed explanation of the solr configurations and how we generated the sort keys for various data types see: https://github.com/projectblacklight/blacklight_browse_nearby/blob/master/SOLR_README.rdoc
99
+
100
+ == Necessary Solr Request Handler
101
+
102
+ You need to make sure there is a terms component setup.
103
+
104
+ <searchComponent name="termsComp" class="solr.TermsComponent"/>
105
+
106
+ Then a request handler needs to be setup using that search component. This request handler matches what is in the default configuration.
107
+
108
+ <requestHandler name="/alphaTerms" class="solr.SearchHandler">
109
+ <lst name="defaults">
110
+ <str name="echoParams">explicit</str>
111
+ <bool name="terms">true</bool>
112
+ <bool name="terms.lower.incl">false</bool>
113
+ <str name="terms.sort">index</str>
114
+ </lst>
115
+ <arr name="components">
116
+ <str>termsComp</str>
117
+ </arr>
118
+ </requestHandler>
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'BlacklightBrowseNearby'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,144 @@
1
+ = Solr for Browse Nearby
2
+
3
+ === Assumptions
4
+ * The Solr documents have a sensible linear sort, and the sort values will be stored in a Solr field.
5
+ * A single Solr document may have more than one sort value.
6
+ * examples
7
+ * Solr document is from a Marc record for multiple items with different call numbers
8
+ * Solr document has an English title and a title in original script
9
+ * Solr document has multiple dates
10
+ * Documents without values in the sort field will not appear in browse nearby.
11
+ * A single sort value may pertain to more than one Solr document. (e.g. there are multiple books with the same title)
12
+ * We have a starting value.
13
+ * We need to look backwards as well as forwards.
14
+ * Sort values may be sparsely distributed
15
+ * Sort values may be clumpy
16
+ * We will know how many documents we want ahead or behind of our starting point, but we won't know the *values* for the sort keys.
17
+ * range queries for large numbers of documents under these circumstances have terrible performance.
18
+
19
+
20
+ === Objectives:
21
+ * Given a starting sort value, we want to get some number of documents ahead and behind the starting value.
22
+ * We want fast Solr response times.
23
+
24
+
25
+ == Sort key
26
+
27
+ For each Solr document, you need an INDEXED Solr field (can be multivalued) for which each value is a SINGLE TOKEN with the NORMALIZED value.
28
+
29
+ Generally, values need to be normalized so they will sort correctly:
30
+
31
+ dates:
32
+
33
+ March 3, 2012 --> 20120303
34
+ 3/4/2012 --> 20120304
35
+ 03/05/2012 --> 20120305
36
+ 13/5/2011 --> 20110513
37
+ 5/14/2011 --> 20110514
38
+ 2010/08/22 --> 20100822
39
+
40
+ titles:
41
+
42
+ Rose --> rose
43
+ The Rose --> rose, the
44
+ A rose --> rose, a
45
+
46
+ Call numbers:
47
+
48
+ HC337 .F5 .F512 --> lc+hc++0337.000000+f0.500000+f0.512000
49
+ TR692 .P37 --> lc+tr++0692.000000+p0.370000+
50
+ M5 .L3 .V2 OP.7:NO.6 1880 --> (I don't want to think about it)
51
+ M5 .L3 K2 .Q2 MD:CRAP0*DMA 1981 --> (I don't want to think about it)
52
+
53
+ Call number parsing can get really nasty. Call number sorting is often no picnic either.
54
+
55
+ There is java code in the SolrMarc project to normalize LC and Dewey call numbers for this purpose:
56
+
57
+ See getLCShelfkey(), getDeweyShelfkey() methods in:
58
+
59
+ https://github.com/solrmarc/stanford-solr-marc/blob/master/core/src/org/solrmarc/tools/CallNumUtils.java or http://code.google.com/p/solrmarc/source/browse/trunk/lib/solrmarc/src/org/solrmarc/tools/CallNumUtils.java
60
+
61
+ Other references:
62
+ * http://code4lib.org/conference/2010/dushay_keck2
63
+ * http://archive.org/details/HowToImplementAVirtualBookshelfWithSolr-NaomiDushayAndJessieKeck
64
+
65
+
66
+ Note that the user facing value for the sort key is generally NOT normalized.
67
+
68
+ == Reverse sort key
69
+
70
+ Solr provides the solr.TermsComponent (http://wiki.apache.org/solr/TermsComponent) which allows us to see the ordered values for a Solr indexed field. However, this only words in the *forward* direction.
71
+
72
+ In order to get the documents before a given sort value, we need a way to use the TermsComponent for values in the reverse order. We can accomplish this by having a field that Solr will sort in the reverse order of the sort key: we call this the reverse sort key.
73
+
74
+ An easy algorithm to get from a sort key to a reverse sort key for alphanum characters:
75
+
76
+ 1. create a map where
77
+ '0' --> 'Z'
78
+ ...
79
+ '9' --> 'Q'
80
+ 'A' --> 'P'
81
+ ...
82
+ 'P' --> 'A'
83
+ 'Q' --> '9'
84
+ ...
85
+ 'Z' --> '0'
86
+
87
+ 2. create a default reverse sort key of some max length where each character is '~'
88
+ 3. for each alphanum character in your sort key, replace the corresponding character in the reverse sort key with the mapped character.
89
+
90
+ There is java code in the SolrMarc project to produce reversed value strings:
91
+
92
+ See getReverseShelfkey() methods in:
93
+
94
+ https://github.com/solrmarc/stanford-solr-marc/blob/master/core/src/org/solrmarc/tools/CallNumUtils.java or http://code.google.com/p/solrmarc/source/browse/trunk/lib/solrmarc/src/org/solrmarc/tools/CallNumUtils.java
95
+
96
+ Other references:
97
+ * http://code4lib.org/conference/2010/dushay_keck2
98
+ * http://archive.org/details/HowToImplementAVirtualBookshelfWithSolr-NaomiDushayAndJessieKeck
99
+
100
+
101
+ == Term lookups for sort keys
102
+
103
+ * Your solrconfig must have the solr.TermsComponent (http://wiki.apache.org/solr/TermsComponent) as a searchComponent
104
+ * Your solrconfig must have a requestHandler that uses that searchComponent
105
+
106
+ <searchComponent name="termsComp" class="solr.TermsComponent"/>
107
+
108
+ <!-- used to get consecutive terms for browsing -->
109
+ <requestHandler name="/alphaTerms" class="solr.SearchHandler">
110
+ <lst name="defaults">
111
+ <str name="echoParams">explicit</str>
112
+ <bool name="terms">true</bool>
113
+ <bool name="terms.lower.incl">true</bool>
114
+ </lst>
115
+ <arr name="components">
116
+ <str>termsComp</str>
117
+ </arr>
118
+ </requestHandler>
119
+
120
+ This will be used to get the next VALUES for the sort key field and the next VALUES for the reverse sort key field.
121
+
122
+ See http://www.stanford.edu/people/~ndushay/code4lib2010/stanford_virtual_shelf.pdf starting on page 21 for more details.
123
+
124
+ Note that the blacklight_browse_nearby Gem will make the Solr requests -- you just need to have the requestHandler in your Solr config, and configure the gem properly.
125
+
126
+
127
+ == Document lookups given sort_key or reverse_sort_key
128
+
129
+ Now that we have the VALUES for our sort keys (and reverse sort keys), we need to get the Solr DOCUMENTS that correspond to these values, as it is the Documents we want to show in our application's UI. This is easily accomplished with a fielded query for the sort key values in the sort key field, and a fielded query for the reverse sort key values in the reverse sort key field.
130
+
131
+
132
+ == Getting a Start Value
133
+
134
+ === from a given Solr document with a single sort key value
135
+
136
+ If you are looking at a single Solr document with a single value for the sort key, this is easy -- use the sort key value!
137
+
138
+ === from a given Solr document with multiple sort key values
139
+
140
+ If there are multiple values in that Solr document for the sort key, you will need to choose one. If you want your users to decide, you will need to be sure you can get the sort key that corresponds to the value displayed to the user. For example, we do not display our shelf keys, but instead our call numbers -- so our Solr documents have a repeatable field containing a call number and its corresponding shelfkey.
141
+
142
+ === from a user entered string
143
+
144
+ You will need to normalize the string in the same way you normalized the sort key values before you can use it for term lookups.
@@ -0,0 +1,12 @@
1
+ $(document).ready(function(){
2
+ $("#blacklight_nearby_items_controls .navigation").live("click",function(){
3
+ $.ajax({url: $(this).attr("href") + "&format=js"});
4
+ return false;
5
+ });
6
+ $("#browse_value_select button").toggle();
7
+ $("#browse_value_select select").live("change", function(){
8
+ $.ajax({url: $("#browse_value_select").attr("action") + "&format=js&preferred_value=" + $(this).val()}).done(function(){
9
+ $("#blacklight_nearby_items_controls .ful_browse").attr("href", $("#blacklight_nearby_items_controls .ful_browse").attr("href") + "&preferred_value=" + $(this).val())
10
+ });
11
+ });
12
+ });
@@ -0,0 +1,11 @@
1
+ #blacklight_nearby_items_container {
2
+ margin-bottom: 10px;
3
+ #blacklight_nearby_items_controls {
4
+ text-align: center;
5
+ }
6
+ #blacklight_nearby_items .item {
7
+ padding:2px;
8
+ margin: 2px 0;
9
+ border:1px solid #CCCCCC;
10
+ }
11
+ }
@@ -0,0 +1,25 @@
1
+ class BlacklightBrowseNearbyController < ApplicationController
2
+ include Blacklight::Catalog
3
+
4
+ before_filter :append_blacklight_catalog_view_context
5
+
6
+ def index
7
+ options = {}
8
+ options[:page] = params[:page] if params[:page]
9
+ options[:number] = params[:per_page] if params[:per_page]
10
+ options[:preferred_value] = params[:preferred_value] if params[:preferred_value]
11
+ @nearby = BlacklightBrowseNearby.new(params[:start], options)
12
+ @document_list = @nearby.documents
13
+ respond_to do |format|
14
+ format.html{ save_current_search_params }
15
+ format.js
16
+ end
17
+
18
+ end
19
+
20
+ protected
21
+
22
+ def append_blacklight_catalog_view_context
23
+ self.view_context.lookup_context.prefixes << "catalog"
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module BlacklightBrowseNearbyHelper
2
+
3
+ def render_nearby_items
4
+ render "blacklight_browse_nearby/nearby_items"
5
+ end
6
+
7
+ end
@@ -0,0 +1,6 @@
1
+ <div class="pagination">
2
+ <ul>
3
+ <li><%= link_to(t('blacklight_browse_nearby.full_view.previous'), nearby_index_path(params.merge(:page => params[:page].to_i-1))) %></li>
4
+ <li><%= link_to(t('blacklight_browse_nearby.full_view.next'), nearby_index_path(params.merge(:page => params[:page].to_i+1))) %></li>
5
+ </ul>
6
+ </div>
@@ -0,0 +1,11 @@
1
+ <div id="blacklight_nearby_items_controls" class="row">
2
+ <div class="span1">
3
+ <%= link_to(t('blacklight_browse_nearby.short_view.previous'), nearby_index_path(params.merge(:id=>nil, :action=> nil, :controller=>nil, :format => nil, :start=>(params[:id] || params[:start]), :page=>params[:page].to_i-1)), :class=>"navigation") %>
4
+ </div>
5
+ <div class="span1">
6
+ <%= link_to(t('blacklight_browse_nearby.short_view.full_view'), nearby_index_path(:preferred_value=> @nearby.current_value , :start=>(params[:id] || params[:start]), :per_page=>BlacklightBrowseNearby::Engine.config.full_view_default_hits)) %>
7
+ </div>
8
+ <div class="span1">
9
+ <%= link_to(t('blacklight_browse_nearby.short_view.next'), nearby_index_path(params.merge(:id=>nil, :action=> nil, :controller=>nil, :format => nil, :start=>(params[:id] || params[:start]), :page=>params[:page].to_i+1)), :class=>"navigation") %>
10
+ </div>
11
+ </div>
@@ -0,0 +1,8 @@
1
+ <div class="item">
2
+ <%= link_to(document[BlacklightBrowseNearby::Engine.config.link_field] ,polymorphic_path(document)) %><br/>
3
+ <%- BlacklightBrowseNearby::Engine.config.nearby_fields.each do |field| -%>
4
+ <%- unless document[field].blank? -%>
5
+ <%= document[field].is_a?(Array) ? document[field].join(", ") : document[field] %><br/>
6
+ <%- end -%>
7
+ <%- end -%>
8
+ </div>
@@ -0,0 +1,22 @@
1
+ <%- blacklight_browse_nearby_items ||= @blacklight_browse_nearby_items -%>
2
+ <%- unless blacklight_browse_nearby_items.blank? -%>
3
+ <div id="blacklight_nearby_items_container">
4
+ <%= t('blacklight_browse_nearby.short_view.browse_text') %>
5
+ <%- if @nearby.potential_values.length > 1 -%>
6
+ <%= form_tag(nearby_index_path(:start=>(params[:id] || params[:start])), :method=>"GET", :id=>"browse_value_select") do %>
7
+ <%= hidden_field_tag :start, (params[:id] || params[:start]) %>
8
+ <%= hidden_field_tag :per_page, BlacklightBrowseNearby::Engine.config.full_view_default_hits %>
9
+ <%= select_tag :preferred_value, options_for_select(@nearby.potential_values, @nearby.current_value), :class=>"span3" %>
10
+ <%= button_tag :submit %>
11
+ <%- end -%>
12
+ <%- else -%>
13
+ <%= @nearby.current_value %>
14
+ <%- end -%>
15
+ <div id="blacklight_nearby_items">
16
+ <%- blacklight_browse_nearby_items.each do |document| -%>
17
+ <%= render :partial => "blacklight_browse_nearby/nearby_item", :locals => {:document=>document} %>
18
+ <%- end -%>
19
+ </div>
20
+ <%= render "blacklight_browse_nearby/nearby_controls" %>
21
+ </div>
22
+ <%- end -%>
@@ -0,0 +1,3 @@
1
+ <%= render "blacklight_browse_nearby/browse_view_pagination" %>
2
+ <%= render @document_list %>
3
+ <%= render "blacklight_browse_nearby/browse_view_pagination" %>
@@ -0,0 +1,2 @@
1
+ $("#blacklight_nearby_items_container").replaceWith("<%= escape_javascript(render(:partial => 'blacklight_browse_nearby/nearby_items', :locals=>{:blacklight_browse_nearby_items => @document_list})) %>");
2
+ $("#browse_value_select button").toggle();
@@ -0,0 +1,15 @@
1
+ <% # header bar for doc items in index view -%>
2
+ <div class="documentHeader">
3
+
4
+ <% # bookmark/folder functions for items/docs -%>
5
+ <%= render_index_doc_actions document %>
6
+
7
+ <% # main title container for doc partial view -%>
8
+ <div class="yui-u">
9
+ <%- if params[:controller] == "blacklight_browse_nearby" -%>
10
+ <h3 class="index_title"><%= link_to_document document, :label=>document_show_link_field %></h3>
11
+ <%- else -%>
12
+ <h3 class="index_title"><%= t('blacklight.search.documents.counter', :counter => (document_counter + 1 + @response.params[:start].to_i)) %><%= link_to_document document, :label=>document_show_link_field, :counter => (document_counter + 1 + @response.params[:start].to_i) %></h3>
13
+ <%- end -%>
14
+ </div>
15
+ </div>
@@ -0,0 +1,11 @@
1
+ en:
2
+ blacklight_browse_nearby:
3
+ full_view:
4
+ previous: '« Previous'
5
+ next: 'Next »'
6
+ short_view:
7
+ browse_text: 'Browsing from:'
8
+ previous: '« Prev'
9
+ next: 'Next »'
10
+ full_view: 'Full View'
11
+