define([ "jquery", "underscore", "backbone", "views/CitationView", "text!templates/citations/citationHeader.html", ], function ($, _, Backbone, CitationView, HeaderTemplate) { "use strict"; /** * @class CitationHeaderView * @classdesc The CitationHeaderView shows a citation information displayed as * a header, with expandable author list (if there are more than a certain * number of authors). * @classcategory Views * @extends CitationView * @screenshot views/CitationHeaderView.png * @since 2.23.0 * @constructor */ var CitationHeaderView = CitationView.extend( /** @lends CitationHeaderView.prototype */ { /** * The name of this type of view * @type {string} */ type: "CitationHeader", /** * See {@link CitationView#className} */ className: "citation header", /** * When there are more than this many authors, then this number of authors * will always be displayed, and the rest will be viewable with a click. * The last author will always be displayed. * @type {number} * @default 20 */ maxAuthors: 20, /** * See {@link CitationView#styles}. This view only uses the header style. * @type {Object} */ styles: { header: { full: { template: _.template(HeaderTemplate), // render: "renderHeader", }, }, }, /** * Override the CitationView style to use the header style. * @see {@link CitationView#style} */ style: "header", /** * Override the CitationView context to use the full context. * @see {@link CitationView#context} */ context: "full", /** * Never create a link for the citation header. * See {@link CitationView#createLink} */ createLink: false, /** * Never create a link for the citation header title. * See {@link CitationView#createTitleLink} */ createTitleLink: false, /** * IDs used in the template to identify the elements that will be * manipulated by this view to make the author list collapsible. * @type {Object} * @property {string} grp1 - The ID of the first group of authors * @property {string} grp2 - The ID of the second group of authors * @property {string} btn - The ID of the button that will toggle the * visibility of the second group of authors * @property {string} last - The ID of the last author * @property {string} ellipsis - The ID of the ellipsis that will be * displayed when the second group of authors is hidden */ classes: { grp1: "CV_authors1", grp2: "CV_authors2", btn: "CV_show-authors", last: "CV_last-author", ellipsis: "CV_ellipsis", }, /** * Tracks whether the list of authors is open or closed. This will be set * automatically when the button is clicked. Set to true when initializing * the view to initially render the list open. * @type {boolean} */ authorListIsOpen: false, /** * Render the citation header. Called by {@link CitationView#render}. * @param {Object} options - Options to pass to the render method * @param {string} options.style - The style to use for rendering */ renderHeader: function (options, template) { const authors = options.originArray.map((author) => { return this.CSLNameToFullNameStr(author); }); // Split the authors into two groups, one with the maximum number of // authors configured (including the last author), and one with the // rest. const numAuthors = (this.numAuthors = authors.length); const maxAuthors = (this.maxAuthors = this.maxAuthors || numAuthors); const authorsGrp1 = (this.authorsGrp1 = authors.slice(0, maxAuthors)); const authorsGrp2 = (this.authorsGrp2 = authors.slice(maxAuthors)); const numAuthorsGrp2 = (this.numAuthorsGrp2 = authorsGrp2.length); // Create a text string for both groups of authors. let grp1Str = ""; let grp2Str = ""; let lastAuthStr = ""; if (numAuthors === 0) { // Keep the strings empty if there are no authors. } else if (numAuthors === 1) { // If there is only one author, just show the name. grp1Str = authorsGrp1[0]; } else if (numAuthors === 2) { // If there are two authors, separate with and. grp1Str = authorsGrp1.join(" and "); } else if (numAuthors <= maxAuthors) { // If there are less than maxAuthors, separate with commas and and. grp1Str = authorsGrp1.slice(0, -1).join(", "); grp1Str += ", and " + authorsGrp1.slice(-1); } else { // Move the last author from grp2 into its own variable const lastAuthor = authorsGrp2.pop(); // Move the last author from grp1 to the start of grp2 authorsGrp2.unshift(authorsGrp1.pop()); // Grp1 string should be separated by just commas, including a comma // at the very end. grp1Str = authorsGrp1.join(", ") + ", "; // Store the "and" with the last author lastAuthStr = " and " + lastAuthor; // grp2 also just commas grp2Str = authorsGrp2.join(", "); } options.authorsGrp1 = grp1Str; options.authorsGrp2 = grp2Str; options.lastAuthor = lastAuthStr; // Pass classes that we can use to refer to all the elements we need to // manipulate. options.classes = this.classes; this.el.innerHTML = template(options); // Select all the elements const els = (this.els = {}); Object.keys(options.classes).forEach((key) => { els[key] = this.el.querySelector(`.${options.classes[key]}`); }); // If there are fewer than maxAuthors, then the template will not render // a button or ellipsis. Otherwise, set the open/close behavior if (els.btn) { els.btn.addEventListener("click", this.toggleList.bind(this)); } if (els.ellipsis) { els.ellipsis.addEventListener("click", this.openList.bind(this)); } if (els.ellipsis || els.btn) { // Set the list to be open or closed based on the initial setting of // in the view if (this.authorListIsOpen) { this.openList(); } else { this.closeList(); } } }, /** * Open the list of authors, showing all authors. */ openList: function () { try { const els = this.els; els.grp2.style.display = ""; els.btn.innerHTML = "- Show fewer authors"; els.ellipsis.style.display = "none"; // Reorder the elements els.btn.parentNode.append( els.grp1, els.grp2, els.last, els.btn, els.ellipsis ); this.authorListIsOpen = true; } catch (error) { console.log( "Failed to expand an author list in the citation view.", error ); } }, /** * Close the list of authors, showing only the first group of authors. The * last author will always be shown. */ closeList: function () { try { const els = this.els; const no = this.numAuthorsGrp2 || ""; els.grp2.style.display = "none"; els.btn.innerHTML = `+ Show ${no} more authors`; els.ellipsis.style.display = ""; // Reorder elements els.btn.parentNode.append( els.grp1, els.ellipsis, els.last, els.btn, els.grp2 ); this.authorListIsOpen = false; } catch (error) { console.log( "Failed to collapse an author list in the citation view.", error ); } }, /** * Toggle the visibility of the second group of authors. If the second * group is visible, then it will be hidden. If the second group is * hidden, then it will be shown. */ toggleList: function () { if (this.authorListIsOpen) { this.closeList(); } else { this.openList(); } }, } ); return CitationHeaderView; });