LP#1424815: 'Read more' accordion in record view
authorKyle Huckins <khuckins@catalyte.io>
Thu, 17 Jan 2019 19:32:45 +0000 (19:32 +0000)
committerGalen Charlton <gmc@equinoxinitiative.org>
Mon, 15 Mar 2021 21:32:38 +0000 (17:32 -0400)
- Introduce custom accordion.JS to handle expand/truncation of
node information.
- Expansion/Truncation of individual nodes based on configurable
length.
- Refactor content.tt2 to properly display multiple notes with both
Read More and Highlighting features.
- Create accordion() macro in summary.tt2 to handle accordion-izing
a string when also supplied with a length, display field string,
and an optional highlighting boolean.
- Implementation of config.tt2 variables to handle enabling and length
requirements for truncation.
- Apply Read More to record Title as well as item details and graphics
text on record and search results.
- Teach Accordion functionality to understand blocks of HTML
- Add release notes for Read More functionality.

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>
Signed-off-by: Ruth Fraser <rfrasur@gmail.com>
Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>

12 files changed:
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/src/templates/opac/css/style.css.tt2
Open-ILS/src/templates/opac/i18n_strings.tt2
Open-ILS/src/templates/opac/parts/config.tt2
Open-ILS/src/templates/opac/parts/js.tt2
Open-ILS/src/templates/opac/parts/misc_util.tt2
Open-ILS/src/templates/opac/parts/record/authors.tt2
Open-ILS/src/templates/opac/parts/record/contents.tt2
Open-ILS/src/templates/opac/parts/record/summary.tt2
Open-ILS/src/templates/opac/parts/result/table.tt2
Open-ILS/web/js/ui/default/opac/accordion.js [new file with mode: 0644]
docs/RELEASE_NOTES_NEXT/OPAC/read-more-feature.adoc [new file with mode: 0644]

index 7f35d18..fdb0da5 100644 (file)
@@ -4,6 +4,8 @@ use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERR
 use File::Spec;
 use Time::HiRes qw/time sleep/;
 use List::MoreUtils qw(uniq);
+use HTML::TreeBuilder;
+use HTML::Element;
 use OpenSRF::Utils::Cache;
 use OpenSRF::Utils::Logger qw/$logger/;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
@@ -130,6 +132,78 @@ sub init_ro_object_cache {
         return (grep {$_->shortname eq $sn} @$list)[0];
     };
 
+    # Turns one string into two for long text strings
+    $locale_subs->{split_for_accordion} = sub {
+        my $html = shift;
+        my $trunc_length = shift;
+
+        return unless defined $html && defined $trunc_length;
+        
+        my $html_string = "";
+        my $trunc_str = "<span class='truncEllipse'>...</span><span class='truncated' style='display:none'>";
+        my $current_length = 0;
+        my $truncated;
+        my @html_strings;
+
+        my $html_tree = HTML::TreeBuilder->new;
+        $html_tree->parse($html);
+        $html_tree->eof();
+
+        # Navigate #html_tree to determine length of contained strings
+        my @nodes = $html_tree->guts();
+        foreach my $node(@nodes) {
+            my $nref = ref $node;
+            if ($nref eq "HTML::Element") {
+                $current_length += length $node->as_text();
+                push(@html_strings, $node->as_HTML());
+            } else {
+                # Node is whitespace - handling this like regular simple text
+                # doesn't like to play nice, so handling separately
+                if ($node eq ' ') { 
+                    $current_length++;
+                    if ($current_length >= $trunc_length and not $truncated) {
+                        push(@html_strings, " $trunc_str");
+                        $truncated = 1;
+                    } else {
+                        push(@html_strings, $node);
+                    }
+                # Node is simple text
+                } else {
+                    my $new_length += length $node;
+                    if ($new_length >= $trunc_length and not $truncated) {
+                        my $nshort;
+                        my $nrest;
+                        my $calc_length = abs($trunc_length - $current_length);
+                        if ((substr $node, $calc_length, 1) =~ /\s/) {
+                            $nshort = substr $node, 0, $calc_length;
+                            $nrest = substr $node, $calc_length;
+                        } else {
+                            my $nloc = rindex $node, ' ', $calc_length;
+                            $nshort = substr $node, 0, $nloc;
+                            $nrest = substr $node, $nloc;
+                        }
+                        push(@html_strings, "$nshort $trunc_str $nrest");
+                        $truncated = 1;
+                    } else {
+                        push(@html_strings, $node);
+                    }
+                    $current_length += length $node;
+                }
+            }
+        }
+        if ($truncated) {
+            push(@html_strings, "</span>");
+        }
+        if (@html_strings > 1) {
+            $html_string = join '', @html_strings;
+        } else {
+            $html_string = $html_strings[0];
+        }
+
+        return ($html_string, $truncated);
+    };
+
     $locale_subs->{aouct_tree} = sub {
 
         # fetch the org unit tree
index 51aa7a6..18cb688 100644 (file)
@@ -49,6 +49,10 @@ a {
     width: 12em;
 }
 
+.truncated {
+    display: none;
+}
+
 .searchbar {
     font-weight: bold;
     padding-top: 10px;
index 7d3b831..2a60c99 100644 (file)
@@ -21,4 +21,7 @@ to js source files, via js blob.
     eg_opac_i18n.EG_INVALID_DATE = "[% l('That is not a valid date in the future.') %]";
     // For multiple holds placement confirmation dialog. {0} is replaced by number of copies requested.
     eg_opac_i18n.EG_MULTIHOLD_MESSAGE = "[% l('Do you really want to place {0} holds for this title?') %]";
+    // For Read More functionality
+    eg_opac_i18n.EG_READ_MORE = "[% l('Read More') %]";
+    eg_opac_i18n.EG_READ_LESS = "[% l('Read Less') %]";
 </script>
index a1438b7..eda51ad 100644 (file)
@@ -276,4 +276,13 @@ ctx.max_cart_size = 500;
 ##############################################################################
 ctx.show_reservations_tab = 'false';
 
+##############################################################################
+# Truncate fields in catalog
+##############################################################################
+truncate_contents = 1;
+contents_truncate_length = 50;
+
+# Edit parts/record/contents.tt2 to designate character length on a field-by-
+# field basis for notes.
+
 %]
index 15ff471..aa4e9f5 100644 (file)
@@ -169,7 +169,7 @@ var aou_hash = {
 [%- IF ctx.max_cart_size; %]
 <script type="text/javascript">var max_cart_size = [% ctx.max_cart_size %];</script>
 [%- END; %]
-
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/opac/accordion.js[% ctx.cache_key %]"></script>
 
 <link rel="stylesheet" href="[% ctx.media_prefix %]/js/ui/default/common/build/js/glide/css/glide.core.min.css[% ctx.cache_key %]">
 <link rel="stylesheet" href="[% ctx.media_prefix %]/js/ui/default/common/build/js/glide/css/glide.theme.min.css[% ctx.cache_key %]">
index 38f2995..ee600a1 100644 (file)
 
 -%]
 
+
 [%- BLOCK carousels;
     config = {
         animated => 0,
 </div>
 [% END -%]
 [% END -%]
+[% MACRO accordion(str, trunc_length, element) BLOCK;
+    IF truncate_contents != 1;
+        str;
+    ELSE;
+        UNLESS trunc_length;
+            trunc_length = contents_truncate_length || 100;
+        END;
+        IF str.length > trunc_length;
+            accordion_res = ctx.split_for_accordion(str, trunc_length);
+            str = accordion_res.0;
+            IF accordion_res.1;
+                str = str _ " <a onclick='toggleAccordion(this, " _ element _ ")'>" _ l('Read More') _ "</a>";
+            END;
+        END;
+        str;
+    END;
+END; %]
index 25dac9a..7062030 100644 (file)
@@ -135,17 +135,26 @@ BLOCK build_author_links;
             END;
             iprop = iprop _ '"';
         END;
-
+        desc_str = '<span property="description">(' _ author_type _ ').</span>';
         link_term = link_term.replace('^\s+', '');
         match_term = link_term _ ' ' _ birthdate _ ' ' _ deathdate;
         matching_author_hl = PROCESS find_hl_value needle=match_term f=attrs.hl_field;
-
-        authtml = ' <span class="rdetail-author-div"' _ iprop _ ' resource="' _ contrib_ref _ '"><a href="' _ url _ '"><span resource="' _ contrib_ref _ '">';
+        authtml = '<span class="rdetail-author-div"' _ iprop _ ' resource="' _ contrib_ref _ '"><a href="' _ url _ '"><span resource="' _ contrib_ref _ '">';
         IF iprop; authtml = authtml _ '<span property="name">'; END;
         IF matching_author_hl;
-            authtml = authtml _ matching_author_hl;
+            IF type == 'cast';
+                link_str = matching_author_hl _ '</span></a> ' _ desc_str _ '</span>';
+                authtml = authtml _ accordion(link_str, null, 'this.previousElementSibling');
+            ELSE;
+                authtml = authtml _ matching_author_hl;
+            END;
         ELSE;
-            authtml = authtml _ link_term;
+            IF type == 'cast';
+                link_str = link_term _ '</span></a> ' _ desc_str _ '</span>';
+                authtml = authtml _ accordion(link_str, null, 'this.previousElementSibling');
+            ELSE;
+                authtml = authtml _ link_term;
+            END;
         END;
         IF iprop; authtml = authtml _ '</span>'; END;
         IF birthdate AND !matching_author_hl;
@@ -154,7 +163,7 @@ BLOCK build_author_links;
         IF deathdate AND !matching_author_hl;
             authtml = authtml _ '<span property="deathDate">' _ deathdate _ '</span>';
         END;
-        authtml = authtml _ '</span></a>'; # End search link
+        IF type != 'cast'; authtml = authtml _ '</span></a>'; END; # End search link
 
         # Display supplemental terms (mostly about the author's work)
         IF supp_term;
@@ -167,12 +176,12 @@ BLOCK build_author_links;
             IF link880.dir;
                 diratt = ' dir="' _ link880.dir _ '"';
             END;
-            authtml = authtml _ ' <span class="graphic880"' _ diratt _ '>';
-            link880.value | html;
-            authtml = authtml _ '</span>';
+            authlist_graphical.push('<span class="graphic880"' _ diratt _ '>' _ link880.value _ '</span>');
+        END;
+        IF type != 'cast';
+            authtml = authtml _ desc_str;
+            authtml = authtml _ '</span>'; # End author span
         END;
-        authtml = authtml _ ' (<span property="description">' _ author_type _ '</span>). ';
-        authtml = authtml _ '</span>'; # End author span
         authlist.push(authtml);
     END;
 END;
@@ -182,14 +191,20 @@ END;
 [%- FOREACH author IN authors;
     NEXT UNLESS author.xpath; 
     authlist = [];
+    authlist_graphical = [];
+    authstr = '';
+    graphical_string = '';
     PROCESS build_author_links(
         xpath=author.xpath, label=author.label, type=author.type
     );
+    IF authlist_graphical.size;
+    %] <div class="content_field"> [%
+        accordion(authlist_graphical.join());
+    %] </div> [% END;
     IF authlist.size;
-        FOREACH authtml IN authlist;
-            authtml;
-        END;
-    END;
+    %] <div class="content_field"> [%
+        accordion(authlist.join());
+    %] </div> [% END;
 END %]
 </div>
 
index db80b47..c63b34c 100644 (file)
@@ -1,8 +1,10 @@
-[%-
+[% USE  Dumper %][%-
+# You can supply a trunc_length parameter to override the default contents_truncate_length value
 contents =  [
     {
         display_field => 'general_note',
         label => l('General Note: '),
+        trunc_length => 100,
         xpath => '//*[@tag="500"]'
     }, {
         label => l('With Note: '),
@@ -177,10 +179,16 @@ BLOCK render_contents;
             all_content.push(subfield.textContent);
         END;
         total_contents = all_content.join(" ").replace('\s+$', '');
-        %] [% "<div class='content_field'>"; total_contents | html ; "</div>";
+        %] [% IF total_contents.size;
+            trunc_length = cont.trunc_length || contents_truncate_length || 100;
+            
+            "<div class='content_field'>"; accordion(total_contents, trunc_length); "</div>";
+        ELSE;
+            "<div class='content_field'>"; accordion(total_contents); "</div>";
+        END;
         FOREACH link880 IN graphics;
             '<div class="graphic880"' _ link880.dir _ '>';
-            link880.value | html;
+            accordion(link880.value);
             '</div>';
         END;
     END;
@@ -188,21 +196,28 @@ END;
 
 BLOCK render_all_contents;
     FOREACH cont IN contents;
+        note_arr = [];
         content = '';
         df = cont.display_field;
-        IF df AND attrs.hl.$df.size;
-            content = '<!-- highlighted -->' _ attrs.hl.$df.join('<br/>');
-        ELSE;
+        trunc_length = cont.trunc_length || contents_truncate_length || 100;
+        IF df AND attrs.hl.$df.size; -%]
+            <tr>
+                <td class='rdetail_content_type'>[% cont.label %]</td>
+                <td class='rdetail_content_value' property='keywords'>
+            [%- FOREACH note IN attrs.hl.$df;
+                "<div class='content_field'>"; accordion(note, trunc_length); "</div>";
+            END -%]
+            </td></tr>
+        [%- ELSE;
             content = PROCESS render_contents(xpath=cont.xpath);
+            IF content.match('\S'); -%]                
+                <tr>
+                    <td class='rdetail_content_type'>[% cont.label %]</td>
+                    <td class='rdetail_content_value' property='keywords'>[% accordion(content, trunc_length) %]</td>
+                </tr>
+            [%- END;
         END;
-        IF content.match('\S');
--%]
-<tr>
-    <td class='rdetail_content_type'>[% cont.label %]</td>
-    <td class='rdetail_content_value' property='keywords'>[% content %]</td>
-</tr>
-        [%- END; %]
-    [%- END; %]
+    END; %]
 [%- END %]
 
 [%-  content_html = PROCESS render_all_contents;
index be248ab..d3aed58 100644 (file)
@@ -3,6 +3,7 @@
     ctx.page_title = attrs.title | html
     ctx.metalinks.push('<meta property="og:image" content="' _ ctx.media_prefix _ '/opac/extras/ac/jacket/large/r/' _ ctx.bre_id _ '" />');
 %]
+
 <!-- ****************** rdetail_summary.xml ***************************** -->
 <abbr class="unapi-id" title='tag:[% ctx.hostname %],[% date.format(date.now, '%Y') %]:biblio-record_entry/[% ctx.bre_id %]'></abbr>
 
@@ -11,7 +12,7 @@
 [%-# This holds the record summary information %]
 <div id="rdetail_summary_header">
     <div id='rdetail_title_div'>
-        <h1 id='rdetail_title' property="name">[% IF attrs.hl.title; attrs.hl.title; ELSE; attrs.title_extended | html; END %]</h1>
+        <h1 id='rdetail_title' property="name">[% IF attrs.hl.title; accordion(attrs.hl.title); ELSE; accordion(attrs.title_extended); END %]</h1>
         [%-
             FOR link880 IN attrs.graphic_titles;
                 FOR alt IN link880.graphic;
@@ -19,7 +20,7 @@
                     IF alt.dir;
                         ' dir="' _ alt.dir _ '"';
                     END;
-                    '>'; alt.value | html; '</h2>';
+                    '>'; accordion(alt.value); '</h2>';
                 END;
             END;
         -%]
@@ -359,22 +360,25 @@ END;
     [%- IF attrs.hl.physical_description.size %]
     <li id='rdetail_phys_desc'>
         <strong class='rdetail_label'>[% l("Physical Description:") %]</strong>
-        <span class='rdetail_value' highlighted='true'>[% attrs.hl.physical_description.join('<br/>') %]</span>
+        <span class='rdetail_value' highlighted='true'>
+        [% FOREACH desc IN attrs.hl.physical_description %]
+            <span>[% accordion(desc) %]</span><br/>
+        [% END %]</span>
     </li>
     [%- ELSIF attrs.phys_desc %]
     <li id='rdetail_phys_desc'>
         <strong class='rdetail_label'>[% l("Physical Description:") %]</strong>
-        <span class='rdetail_value'>[% attrs.phys_desc | html %]</span>
+        <span class='rdetail_value'>[% accordion(attrs.phys_desc) %]</span>
     </li>
     [%- END %]
     [%- IF attrs.hl.edition %]
     <li id='rdetail_edition'>
         <strong class='rdetail_label'>[% l("Edition:") %]</strong>
-        <span class='rdetail_value' highlighted='true'>[% attrs.hl.edition %]</span>
+        <span class='rdetail_value' highlighted='true'>[% accordion(attrs.hl.edition) %]</span>
     [%- ELSIF attrs.edition %]
     <li id='rdetail_edition'>
         <strong class='rdetail_label'>[% l("Edition:") %]</strong>
-        <span class='rdetail_value'>[% attrs.edition | html %]</span>
+        <span class='rdetail_value'>[% accordion(attrs.edition) %]</span>
         [%-
         FOR entry IN attrs.graphic_editions;
             FOR alt IN entry.graphic;
@@ -384,7 +388,7 @@ END;
                 END;
         -%]
         <div class="graphic880 rdetail_value"[% diratt %]>
-            [% alt.value | html %]
+            [% accordion(alt.value); %]
         </div>
         [%-
             END;
@@ -395,19 +399,20 @@ END;
     [%- IF attrs.hl.publisher %]
     <li id='rdetail_publisher'>
         <strong class='rdetail_label'>[% l("Publisher:") %]</strong>
-        <span class='rdetail_value' highlighted='true'>[% attrs.hl.publisher %]</span>
+        <span class='rdetail_value' highlighted='true'>[% accordion(attrs.hl.publisher) %]</span>
     </li>
     [%- ELSIF attrs.publisher %]
     <li id='rdetail_publisher'>
         <strong class='rdetail_label'>[% l("Publisher:") %]</strong>
         <span class='rdetail_value' property="publisher" typeof="Organization">
+        [% pubstr = '' %]
         [%- IF attrs.pubplace; %]
-            <span property="location">[% attrs.pubplace | html; %]</span>
+            <span property="location">[% attrs.pubplace %]</span>
         [%- END; %]
-            <span property="name">[% attrs.publisher | html; %]</span>
+            <span property="name">[% attrs.publisher %]</span>
         </span>
         [%- IF attrs.pubdate; %]
-            <span property="datePublished">[% attrs.pubdate | html; %]</span>
+            <span property="datePublished">[% attrs.pubdate %]</span>
         [%- END; %]
         [%-
         IF attrs.graphic_pubinfos.size > 0;
@@ -419,7 +424,7 @@ END;
                     END;
         -%]
         <div class="graphic880"[% diratt %]>
-            [% alt.value | html %]
+            [% accordion(alt.value) %]
         </div>
         [%-
                 END;
@@ -433,11 +438,11 @@ END;
             <strong class='rdetail_label'>[% l("Producer:") %]</strong>
             <span class='rdetail_value'>
             [%- IF attrs.prodplace; %]
-                <span>[% attrs.prodplace | html; %]</span>
+                <span>[% accordion(attrs.prodplace) %]</span>
             [%- END; %]
-                <span>[% attrs.producer | html; %]</span>
+                <span>[% accordion(attrs.producer) %]</span>
             [%- IF attrs.proddate; %]
-                <span>[% attrs.proddate | html; %]</span>
+                <span>[% accordion(attrs.proddate) %]</span>
             [%- END; %]
             </span>
         </li>
@@ -447,11 +452,11 @@ END;
             <strong class='rdetail_label'>[% l("Distributor:") %]</strong>
             <span class='rdetail_value'>
             [%- IF attrs.distplace; %]
-                <span>[% attrs.distplace | html; %]</span>
+                <span>[% accordion(attrs.distplace) %]</span>
             [%- END; %]
-                <span>[% attrs.distributor | html; %]</span>
+                <span>[% accordion(attrs.distributor) %]</span>
             [%- IF attrs.distdate; %]
-                <span>[% attrs.distdate | html; %]</span>
+                <span>[% accordion(attrs.distdate) %]</span>
             [%- END; %]
             </span>
         </li>
@@ -461,11 +466,11 @@ END;
             <strong class='rdetail_label'>[% l("Manufacturer:") %]</strong>
             <span class='rdetail_value' property="manufacturer" typeof="Organization">
             [%- IF attrs.manplace; %]
-                <span property="location">[% attrs.manplace | html; %]</span>
+                <span property="location">[% accordion(attrs.manplace) %]</span>
             [%- END; %]
-                <span property="name">[% attrs.manufacturer | html; %]</span>
+                <span property="name">[% accordion(attrs.manufacturer) %]</span>
             [%- IF attrs.mandate; %]
-                <span>[% attrs.mandate | html; %]</span>
+                <span>[% accordion(attrs.mandate) %]</span>
             [%- END; %]
             </span>
         </li>
@@ -473,7 +478,7 @@ END;
     [%- IF attrs.copyright %]
     <li id='rdetail_copyright'>
         <strong class='rdetail_label'>[% l("Copyright:") %]</strong>
-        <span class='rdetail_value'>[% attrs.copyright | html_entity; %]
+        <span class='rdetail_value'>[% accordion(attrs.copyright) %]
         [%-# Provide the 4-digit year, cleansed of '@' and other junk %]
         [%- IF attrs.copyrightYear -%]
             <meta property='copyrightYear' content='[% attrs.copyrightYear | html; %]'>
index c9436f9..0597940 100644 (file)
@@ -150,7 +150,7 @@ FOR entry IN attrs.graphic_titles;
         END;
 -%]
 <div class="graphic880"[% diratt %]>
-    [% alt.value | html %]
+    [% accordion(alt.value) %]
 </div>
 [%-
     END;
@@ -173,7 +173,7 @@ FOR entry IN attrs.graphic_authors;
         END;
 -%]
 <div class="graphic880"[% diratt %]>
-    [% alt.value | html %]
+    [% accordion(alt.value) %]
 </div>
 [%-
     END;
@@ -230,7 +230,8 @@ END;
                                                                 <td valign="top">
                                                                     <strong>[% l('Publisher:') %]</strong>
                                                                 </td>
-                                                                <td>[% attrs.pubplace | html; %] [% attrs.publisher | html; %] [% attrs.pubdate | html; %]
+                                                                <td>[% pub_string = attrs.pubplace _ attrs.publisher _ attrs.pubdate;
+                                                                        accordion(pub_string, 'publisher'); %]</td>
 [%-
 FOR entry IN attrs.graphic_pubinfos;
     FOR alt IN entry.graphic;
@@ -240,7 +241,7 @@ FOR entry IN attrs.graphic_pubinfos;
         END;
 -%]
 <div class="graphic880"[% diratt %]>
-    [% alt.value | html %]
+    [% accordion(alt.value) %]
 </div>
 [%-
     END;
@@ -253,21 +254,24 @@ END;
                                                                 <td valign="top">
                                                                     <strong>[% l('Producer:') %]</strong>
                                                                 </td>
-                                                                <td>[% attrs.prodplace | html; %] [% attrs.producer | html; %] [% attrs.proddate | html; %]</td>
+                                                                <td>[% prod_string = attrs.prodplace _ attrs.producer _ attrs.proddate;
+                                                                 accordion(prod_string, 'producer'); %]</td>
                                                             </tr>
                                                         [% ELSIF attrs.distributor %]
                                                             <tr name="results_pub_tr">
                                                                 <td valign="top">
                                                                     <strong>[% l('Distributor:') %]</strong>
                                                                 </td>
-                                                                <td>[% attrs.distplace | html; %] [% attrs.distributor | html; %] [% attrs.distdate | html; %]</td>
+                                                                <td>[% dist_string = attrs.distplace _ attrs.distributor _ attrs.distdate; 
+                                                                  accordion(dist_string, 'distributor'); %]</td>
                                                             </tr>
                                                         [% ELSIF attrs.manufacturer %]
                                                             <tr name="results_pub_tr">
                                                                 <td valign="top">
                                                                     <strong>[% l('Manufacturer:') %]</strong>
                                                                 </td>
-                                                                <td>[% attrs.manplace | html; %] [% attrs.manufacturer | html; %] [% attrs.mandate | html; %]</td>
+                                                                <td>[% man_string = attrs.manplace _ attrs.manufacturer _ attrs.mandate;
+                                                                  accordion(man_string, 'manufacturer'); %]</td>
                                                             </tr>
                                                         [% END %]
                                                         [% IF attrs.isbns.size > 0 %]
@@ -314,7 +318,7 @@ END;
                                                                 <td valign="top">
                                                                     <strong>[% l('Edition:') %]</strong>
                                                                 </td>
-                                                                <td>[% attrs.edition | html %]
+                                                                <td>[% accordion(attrs.edition, 'edition'); %]
 [%-
 FOR entry IN attrs.graphic_editions;
     FOR alt IN entry.graphic;
@@ -324,7 +328,7 @@ FOR entry IN attrs.graphic_editions;
         END;
 -%]
 <div class="graphic880"[% diratt %]>
-    [% alt.value | html %]
+    [% accordion(alt.value) %]
 </div>
 [%-
     END;
@@ -339,7 +343,7 @@ END;
                                                                     <strong>[% l('Phys. Desc.:') %]</strong>
                                                                 </td>
                                                                 <td>
-                                                                    [% args.phys_desc | html %]
+                                                                    [% accordion(args.phys_desc, 'phys_desc'); %]
                                                                 </td>
                                                             </tr>
                                                         [% END %]
diff --git a/Open-ILS/web/js/ui/default/opac/accordion.js b/Open-ILS/web/js/ui/default/opac/accordion.js
new file mode 100644 (file)
index 0000000..5b45085
--- /dev/null
@@ -0,0 +1,47 @@
+function toggleAccordion(elm, alternate_elm) {
+    var truncatedSpan;
+    var ellipse;
+    
+    if (!alternate_elm) {
+        var children = getSiblings(elm);
+        for (i = 0; i < children.length; i++) {
+            if (children[i].className == "truncated") {
+                truncatedSpan = children[i];
+            } else if (children[i].className == "truncEllipse") {
+                ellipse = children[i];
+            }
+        }
+    } else {
+        truncatedSpan = iterateChildren(alternate_elm, 'truncated');
+        ellipse = iterateChildren(alternate_elm, 'truncEllipse');
+    }
+
+    if (truncatedSpan.style.display == "none") {
+        truncatedSpan.style.display = "inline";
+        elm.innerHTML = eg_opac_i18n.EG_READ_LESS;
+        ellipse.style.display = "none";
+    } else {
+        truncatedSpan.style.display = "none";
+        elm.innerHTML = eg_opac_i18n.EG_READ_MORE;
+        ellipse.style.display = "inline";
+    }
+}
+
+function getSiblings(elm) {
+    return Array.prototype.filter.call(elm.parentNode.children, function (sibling) {
+               return sibling !== elm;
+       });
+}
+
+function iterateChildren(elm, classname) {
+    var child_to_return;
+    if (elm.className == classname) return elm;
+    for (i = 0; i < elm.children.length; i++) {
+        if (elm.children[i].className == classname) {
+            return elm.children[i];
+        } else {
+            child_to_return = iterateChildren(elm.children[i], classname);
+        }
+    }
+    if (child_to_return) return child_to_return;
+}
\ No newline at end of file
diff --git a/docs/RELEASE_NOTES_NEXT/OPAC/read-more-feature.adoc b/docs/RELEASE_NOTES_NEXT/OPAC/read-more-feature.adoc
new file mode 100644 (file)
index 0000000..9acb82f
--- /dev/null
@@ -0,0 +1,31 @@
+Configurable Read More Accordion for OPAC Search and Record View\r
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r
+\r
+Read More Button\r
+++++++++++++++++\r
+OPAC record Fields now truncate themselves based on a configurable amount of characters.\r
+The full field may be displayed upon hitting a (Read More) link, which will then toggle\r
+into a (Read Less) link to re-truncate the field.\r
+\r
+Configuration\r
++++++++++++++\r
+Open-ILS/src/templates/opac/parts/config.tt2 Contains two new configuration variables:\r
+truncate_contents(default: 1) and contents_truncate_length(default: 50). Setting\r
+truncate_contents to 0 will disable the read more functionality.\r
+The variable contents_truncate_length corresponds to the amount of characters to display\r
+before truncating the text. If contents_truncate_length is removed, it will default to 100.\r
+Additional configuration for note fields can be made in\r
+Open-ILS/src/templates/opac/parts/record/contents.tt2, allowing a trunc_length variable for\r
+each individual type of note, which will override contents_truncate_length for that specific\r
+type of note.\r
+\r
+\r
+Adding Read More Functionality to further fields\r
+++++++++++++++++++++++++++++++++++++++++++++++++\r
+To add Read More functionality to any additional fields, you may use the macro\r
+accordion(), defined in misc_util.tt2. It can take three variables: str,\r
+trunc_length, and element. str corresponds to the string you want to apply it to,\r
+trunc_length(optional) will override contents_truncate_length if supplied, and\r
+element(optional) provides an alternative HTML element to look at for the truncation\r
+process(useful in situations such as the Authors and Cast fields, where each field is\r
+processed individually, but needs to be treated as a single field).
\ No newline at end of file