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;
});