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/;
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
width: 12em;
}
+.truncated {
+ display: none;
+}
+
.searchbar {
font-weight: bold;
padding-top: 10px;
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>
##############################################################################
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.
+
%]
[%- 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 %]">
-%]
+
[%- 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; %]
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;
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;
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;
[%- 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>
-[%-
+[% 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: '),
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;
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;
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>
[%-# 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;
IF alt.dir;
' dir="' _ alt.dir _ '"';
END;
- '>'; alt.value | html; '</h2>';
+ '>'; accordion(alt.value); '</h2>';
END;
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;
END;
-%]
<div class="graphic880 rdetail_value"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value); %]
</div>
[%-
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;
END;
-%]
<div class="graphic880"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value) %]
</div>
[%-
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>
<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>
<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>
[%- 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; %]'>
END;
-%]
<div class="graphic880"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value) %]
</div>
[%-
END;
END;
-%]
<div class="graphic880"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value) %]
</div>
[%-
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;
END;
-%]
<div class="graphic880"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value) %]
</div>
[%-
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 %]
<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;
END;
-%]
<div class="graphic880"[% diratt %]>
- [% alt.value | html %]
+ [% accordion(alt.value) %]
</div>
[%-
END;
<strong>[% l('Phys. Desc.:') %]</strong>
</td>
<td>
- [% args.phys_desc | html %]
+ [% accordion(args.phys_desc, 'phys_desc'); %]
</td>
</tr>
[% END %]
--- /dev/null
+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
--- /dev/null
+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