diff --git a/README.md b/README.md
index 2fe1a976..e6af288f 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
**Lens** provides a novel way of looking at content on the web. It is designed to make life easier for researchers, reviewers, authors and readers.
-- **Read the [announcement](http://elifesciences.org/elife-news/lens)**
-- **Watch the [introduction video](http://vimeo.com/67254579).**
-- **See Lens in [action](http://lens.elifesciences.org/00778)**
+- **Read the [announcement](https://elifesciences.org/elife-news/lens)**
+- **Watch the [introduction video](https://vimeo.com/67254579).**
+- **See Lens in [action](https://lens.elifesciences.org/00778)**
## Using Lens
@@ -29,7 +29,7 @@ However, now let's look into developing our own extensions.
### Prerequisites
-For Lens development, you need to have Node.js >=0.10.x installed.
+For Lens development, you need to have Node.js >=10.x installed.
You need to repeat that install step whenever you updated the screwdriver repo.
@@ -37,25 +37,37 @@ You need to repeat that install step whenever you updated the screwdriver repo.
1. Clone the `lens-starter` repository
- ```bash
- $ git clone https://github.com/elifesciences/lens-starter.git
- ```
+```bash
+ git clone https://github.com/elifesciences/lens-starter.git
+ cd lens-starter
+```
+
+2. Configure System
-2. Fetch dependencies
+As stated above, you'll need version 10.x of Node installed, and you'll also need version 2.7.x of Python available. You can use [nvm](https://github.com/nvm-sh/nvm) to manage which version of node to use on a per-project basis, and [PyEnv](https://github.com/pyenv/pyenv) to do the same for Python. With both of these tools setup, you can...
- ```bash
- $ cd lens-starter
- $ npm install
- ```
+```bash
+echo "lts/dubnium" > .nvmrc
+nvm install
+nvm use
+echo "2.7.17" > .python-version
+pyenv install
+pvenv local
+```
-3. Run the server
+3. Fetch dependencies
- ```bash
- ~/projects/lens-starter $ node server
- Lens running on port 4001
- http://127.0.0.1:4001/
- ```
+```bash
+npm install
+```
+
+4. Run the server
+
+```bash
+npm start
+```
+Then navigate to http://127.0.0.1:4001/ in your web browser.
### Converter
@@ -94,7 +106,7 @@ ElifeConverter.Prototype = function() {
return [baseURL, node.url].join('');
} else {
node.url = [
- "http://cdn.elifesciences.org/elife-articles/",
+ "https://cdn.elifesciences.org/elife-articles/",
state.doc.id,
"/suppl/",
node.url
@@ -203,7 +215,7 @@ Lens can easily be extended with a customized panel. It can be used to show addi
- Pull in metrics (click count, number of articles citing that article etc.)
- Retrieve related articles dynamically (e.g. important ones that reference the existing one)
-For demonstration we will look at the implementation of a simple Altmetrics panel. It will pull data asynchronously from the Altmetrics API (http://api.altmetric.com/v1/doi/10.7554/eLife.00005) and render the information in Lens.
+For demonstration we will look at the implementation of a simple Altmetrics panel. It will pull data asynchronously from the Altmetrics API (https://api.altmetric.com/v1/doi/10.7554/eLife.00005) and render the information in Lens.
#### Panel Definition
@@ -241,7 +253,7 @@ AltmetricsController.Prototype = function() {
var doi = this.document.get('publication_info').doi;
$.ajax({
- url: "http://api.altmetric.com/v1/doi/"+doi,
+ url: "https://api.altmetric.com/v1/doi/"+doi,
dataType: "json",
}).done(function(res) {
cb(null, res);
@@ -335,9 +347,9 @@ Mobile support has been removed with Lens 2.0 to reduce technical debt and itera
## Credits
-Lens was developed in collaboration between [UC Berkeley](http://bioegrad.berkeley.edu/) graduate student [Ivan Grubisic](http://www.linkedin.com/pub/ivan-grubisic/26/353/739) and [eLife](http://elifesciences.org). The team of [Substance](http://substance.io) is helping with the technical execution.
+Lens was developed in collaboration between [UC Berkeley](http://bioegrad.berkeley.edu/) graduate student [Ivan Grubisic](https://www.linkedin.com/pub/ivan-grubisic/26/353/739) and [eLife](https://elifesciences.org). The team of [Substance](http://substance.io) is helping with the technical execution.
-Substantial contributions were made by [HighWire](highwire.org), which launched Lens for a number of science journals in fall 2014 (The Journal of Biological Chemistry, The Plant Cell, Journal of Lipid Research, mBio®, and more). [The American Mathematical Society (AMS)](http://ams.org/) made Lens ready for advanced rendering of math articles.
+Substantial contributions were made by [HighWire](http://highwire.org), which launched Lens for a number of science journals in fall 2014 (The Journal of Biological Chemistry, The Plant Cell, Journal of Lipid Research, mBio®, and more). [The American Mathematical Society (AMS)](http://ams.org/) made Lens ready for advanced rendering of math articles.
Thanks go to the following people, who made Lens possible:
diff --git a/article/README.md b/article/README.md
index 943fe3c1..1e2bec7d 100644
--- a/article/README.md
+++ b/article/README.md
@@ -1,7 +1,7 @@
Lens Article
=====
-The Lens Article Format is an implementation the [Substance Document Model](http://github.com/substance/document) dedicated to scientific content. It features basic content types such as paragraphs, headings, and various figure types such as images, tables and videos complete with captions and cross-references.
+The Lens Article Format is an implementation the [Substance Document Model](https://github.com/substance/document) dedicated to scientific content. It features basic content types such as paragraphs, headings, and various figure types such as images, tables and videos complete with captions and cross-references.
The document defintions can be extended easily, so you can either create your own flavour or contribute to the Lens Article Format directly.
@@ -9,4 +9,4 @@ The document defintions can be extended easily, so you can either create your ow
- XML-based formats such as NML are hard to consume by webclients
- Strict separation of content and style. Existing formats target print, and thus contain style information, which makes them hard to process by computer programs
-- The greatest advantage of Lens Articles is that any of them can be viewed in [Lens](http://github.com/elifesciences/lens), a modern web-based interface for consuming science content.
+- The greatest advantage of Lens Articles is that any of them can be viewed in [Lens](https://github.com/elifesciences/lens), a modern web-based interface for consuming science content.
diff --git a/article/article_util.js b/article/article_util.js
index 8b40e272..041076f3 100644
--- a/article/article_util.js
+++ b/article/article_util.js
@@ -19,9 +19,9 @@ util.formatDate = function (pubDate) {
var parts = pubDate.split("-");
if (parts.length >= 3) {
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
- // Note: months are 0-based
+ // Note: months are 0-based, which are stripped using a regexp
var localDate = new Date(parts[0], parts[1]-1, parts[2]);
- return localDate.toUTCString().slice(0, 16);
+ return localDate.toDateString().slice(4, 16).replace(/\b0+/g, '')
} else if (parts.length === 2) {
var month = parts[1].replace(/^0/, "");
var year = parts[0];
diff --git a/article/nodes/_affiliation/affiliation.js b/article/nodes/_affiliation/affiliation.js
index 9fc038ce..bab60604 100644
--- a/article/nodes/_affiliation/affiliation.js
+++ b/article/nodes/_affiliation/affiliation.js
@@ -15,7 +15,8 @@ Affiliation.type = {
"country": "string",
"department": "string",
"institution": "string",
- "label": "string"
+ "label": "string",
+ "specific_use": "string"
}
};
diff --git a/article/nodes/citation/citation.js b/article/nodes/citation/citation.js
index 47aa1c64..60b943de 100644
--- a/article/nodes/citation/citation.js
+++ b/article/nodes/citation/citation.js
@@ -90,7 +90,7 @@ Citation.example = {
"citation_urls": [
{
"name": "PubMed",
- "url": "http://www.ncbi.nlm.nih.gov/pubmed/19606141"
+ "url": "https://www.ncbi.nlm.nih.gov/pubmed/19606141"
}
]
};
diff --git a/article/nodes/composite/composite_view.js b/article/nodes/composite/composite_view.js
index cb4cb8d1..9fbb9eea 100644
--- a/article/nodes/composite/composite_view.js
+++ b/article/nodes/composite/composite_view.js
@@ -16,10 +16,6 @@ CompositeView.Prototype = function() {
// =============================
//
- // Render Markup
- // --------
- //
-
this.render = function() {
NodeView.prototype.render.call(this);
diff --git a/article/nodes/contributor/contributor.css b/article/nodes/contributor/contributor.css
index 5ff11687..d6c66b49 100644
--- a/article/nodes/contributor/contributor.css
+++ b/article/nodes/contributor/contributor.css
@@ -1,5 +1,5 @@
/*
-Contributor
+Contributor
--------------------------------------- */
.lens-article .resources .content-node.contributor .resource-header .name {
@@ -9,16 +9,16 @@ Contributor
}
.lens-article .content-node.contributor .content {
-
+
}
.lens-article .content-node.contributor .affiliation {
- margin-top: 10px;
- font-size: 14px;
+ margin-top: 12px;
+ margin-bottom: 12px;
}
.lens-article .content-node.contributor .contributor-bio {
- padding-top: 30px;
+ padding-top: 30px;
}
.lens-article .content-node.contributor .contributor-bio .bio {
@@ -44,8 +44,10 @@ Contributor
margin-bottom: 20px;
}
-.lens-article .content-node.contributor .label {
- font-size: 14px;
- margin-top: 20px;
- color: #999;
-}
\ No newline at end of file
+.lens-article .content-node.contributor .contrib-label {
+ font-weight: 600;
+}
+
+.lens-article .content-node.contributor .contrib-data {
+ margin-bottom: 12px;
+}
diff --git a/article/nodes/contributor/contributor_view.js b/article/nodes/contributor/contributor_view.js
index 51ce0da7..ed25daaf 100644
--- a/article/nodes/contributor/contributor_view.js
+++ b/article/nodes/contributor/contributor_view.js
@@ -35,9 +35,9 @@ ContributorView.Prototype = function() {
// -------
if (this.node.role) {
- this.content.appendChild($$('.role', {text: this.node.role}));
+ this.content.appendChild($$('.role', {text: this.node.role}));
}
-
+
// Add Affiliations
// -------
@@ -57,29 +57,47 @@ ContributorView.Prototype = function() {
}));
-
// Present Address
// -------
if (this.node.present_address) {
- this.content.appendChild($$('.label', {text: 'Present address'}));
- this.content.appendChild($$('.contribution', {text: this.node.present_address}));
+ this.content.appendChild(
+ $$('.present-address.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Present address: '}),
+ $$('span', {text: this.node.present_address})
+ ]
+ })
+ );
}
// Contribution
// -------
if (this.node.contribution) {
- this.content.appendChild($$('.label', {text: 'Contribution'}));
- this.content.appendChild($$('.contribution', {text: this.node.contribution}));
+ this.content.appendChild(
+ $$('.contribution.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Contribution: '}),
+ $$('span', {text: this.node.contribution})
+ ]
+ })
+ );
}
// Equal contribution
// -------
+
if (this.node.equal_contrib && this.node.equal_contrib.length > 0) {
- this.content.appendChild($$('.label', {text: 'Contributed equally with'}));
- this.content.appendChild($$('.equal-contribution', {text: this.node.equal_contrib}));
+ this.content.appendChild(
+ $$('.equal-contribution.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Contributed equally with: '}),
+ $$('span', {text: this.node.equal_contrib.join(', ')})
+ ]
+ })
+ );
}
@@ -87,38 +105,51 @@ ContributorView.Prototype = function() {
// -------
if (this.node.emails.length > 0) {
- this.content.appendChild($$('.label', {text: 'For correspondence'}));
- this.content.appendChild($$('.emails', {
- children: _.map(this.node.emails, function(email) {
- return $$('a', {href: "mailto:"+email, text: email});
+ this.content.appendChild(
+ $$('.emails.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'For correspondence: '}),
+ $$('span', {
+ children: _.map(this.node.emails, function(email) {
+ return $$('a', {href: "mailto:"+email, text: email+' '});
+ })
+ })
+ ]
})
- }));
+ );
}
-
// Funding
// -------
if (this.node.fundings.length > 0) {
- this.content.appendChild($$('.label', {text: 'Funding'}));
- this.content.appendChild($$('.fundings', {
- children: _.map(this.node.fundings, function(funding) {
- return $$('.funding', {text: funding});
+ this.content.appendChild(
+ $$('.fundings.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Funding: '}),
+ $$('span', {
+ text: this.node.fundings.join('; ')
+ })
+ ]
})
- }));
+ );
}
-
// Competing interests
// -------
- if (this.node.competing_interests.length > 0) {
- this.content.appendChild($$('.label', {text: 'Competing Interests'}));
- this.content.appendChild($$('.competing-interests', {
- children: _.map(this.node.competing_interests, function(ci) {
- return $$('.conflict', {text: ci});
+
+ if (this.node.competing_interests.length) {
+ this.content.appendChild(
+ $$('.competing-interests.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Competing Interests: '}),
+ $$('span', {
+ text: this.node.competing_interests.join(', ')
+ })
+ ]
})
- }));
+ );
}
@@ -126,24 +157,34 @@ ContributorView.Prototype = function() {
// -------
if (this.node.orcid) {
- this.content.appendChild($$('.label', { text: 'ORCID' }));
- this.content.appendChild($$('a.orcid', { href: this.node.orcid, text: this.node.orcid }));
+ this.content.appendChild(
+ $$('.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'ORCID: '}),
+ $$('a.orcid', { href: this.node.orcid, text: this.node.orcid })
+ ]
+ })
+ );
}
+
// Group member (in case contributor is a person group)
// -------
if (this.node.members.length > 0) {
- this.content.appendChild($$('.label', {text: 'Group Members'}));
- this.content.appendChild($$('.members', {
- children: _.map(this.node.members, function(member) {
- return $$('.member', {text: member});
+ this.content.appendChild(
+ $$('.group-members.contrib-data', {
+ children: [
+ $$('span.contrib-label', {text: 'Group Members: '}),
+ $$('span', {
+ text: this.node.members.join(', ')
+ })
+ ]
})
- }));
+ );
}
-
// Contributor Bio
// -------
@@ -164,7 +205,6 @@ ContributorView.Prototype = function() {
// -------
if (this.node.deceased) {
- // this.content.appendChild($$('.label', {text: 'Present address'}));
this.content.appendChild($$('.label', {text: "* Deceased"}));
}
diff --git a/article/nodes/cover/cover.css b/article/nodes/cover/cover.css
index 1b0d758c..ab03b997 100644
--- a/article/nodes/cover/cover.css
+++ b/article/nodes/cover/cover.css
@@ -1,22 +1,5 @@
.lens-article .content-node.cover {
- color: rgba(0,0,0,0.8);
-}
-
-.lens-article .content-node.cover .breadcrumbs {
- font-size: 14px;
- margin-top: 30px;
- margin-bottom: 20px;
-
- /* Prevent from nasty scrollbars that appear when eLife logo is shown */
- overflow: hidden;
-}
-
-.lens-article .content-node.cover .breadcrumbs a {
- margin-right: 20px;
- display: block;
- float: left;
- line-height: 40px;
- height: 40px;
+ text-align: center;
}
.lens-article .content-node.cover .content {
@@ -30,16 +13,26 @@
padding-top: 20px;
}
+.lens-article .content-node.cover .subjects {
+
+}
+
.lens-article .content-node.cover .published-on {
- margin-top: 50px;
- margin-bottom: 20px;
- color: #666;
+ color: #999;
+}
+
+.lens-article .content-node.cover .published-on a {
+ font-weight: 600;
+ color: #999;
+}
+
+.lens-article .content-node.cover .published-on a:hover {
+ color: #0277BD;
}
.lens-article .content-node.cover .doi {
margin-top: 30px;
margin-bottom: 20px;
- color: #666;
font-size: 14px;
}
@@ -47,6 +40,7 @@
padding-top: 30px;
color: #1B6685;
overflow: auto;
+ margin-bottom: 24px;
}
.lens-article .content-node.cover .content .links {
@@ -61,12 +55,12 @@
/* One para per author */
.lens-article .content-node.cover .authors .text {
- float: left;
+ display: inline-block;
padding: 0px;
margin: 0px;
font-size: 17px;
margin-right: 10px;
- margin-bottom: 8px;
+ margin-bottom: 0px;
}
.lens-article .content-node.cover .authors .text.plain {
@@ -74,7 +68,6 @@
padding-left: 1px;
}
-
.lens-article .intro {
font-size: 13px;
background: #FFFEF5;
@@ -96,4 +89,4 @@
.lens-article .intro .send-feedback:hover {
color: #ff0000;
-}
\ No newline at end of file
+}
diff --git a/article/nodes/cover/cover_view.js b/article/nodes/cover/cover_view.js
index 02c26d92..49ac45c3 100644
--- a/article/nodes/cover/cover_view.js
+++ b/article/nodes/cover/cover_view.js
@@ -30,33 +30,28 @@ CoverView.Prototype = function() {
var node = this.node;
var pubInfo = this.node.document.get('publication_info');
- if (node.breadcrumbs && node.breadcrumbs.length > 0) {
- var breadcrumbs = $$('.breadcrumbs', {
- children: _.map(node.breadcrumbs, function(bc) {
- var html;
- if (bc.image) {
- html = '';
- } else {
- html = bc.name;
- }
- return $$('a', {href: bc.url, html: html});
- })
- });
- this.content.appendChild(breadcrumbs);
- }
+ // Render Subject(s) if available
+ // --------------
+ //
if (pubInfo) {
- var pubDate = pubInfo.published_on;
- if (pubDate) {
- var items = [articleUtil.formatDate(pubDate)];
- if (pubInfo.journal && !node.breadcrumbs) {
- items.push(' in '+pubInfo.journal+'');
- }
+ var subjects = pubInfo.subjects;
+ if (subjects) {
+ var subjectsEl
+ if (pubInfo.subject_link) {
+ subjectsEl = $$('.subjects', {
+ children: _.map(pubInfo.getSubjectLinks(), function(subject) {
+ return $$('a', {href: subject.url, text: subject.name})
+ })
+ })
- this.content.appendChild($$('.published-on', {
- html: items.join('')
- }));
+ } else {
+ subjectsEl = $$('.subjects', {
+ html: subjects.join(' ')
+ })
+ }
+ this.content.appendChild(subjectsEl);
}
}
@@ -88,6 +83,28 @@ CoverView.Prototype = function() {
this.content.appendChild(authors);
+ if (pubInfo) {
+ var pubDate = pubInfo.published_on;
+ var articleType = pubInfo.article_type;
+ if (pubDate) {
+ var items = [articleUtil.formatDate(pubDate)];
+
+ if (articleType) {
+ if (pubInfo.article_type_link) {
+ var linkData = pubInfo.getArticleTypeLink()
+ items.unshift(''+linkData.name+'')
+ } else {
+ items.unshift(articleType)
+ }
+
+ }
+
+ this.content.appendChild($$('.published-on', {
+ html: items.join(' ')
+ }));
+ }
+ }
+
// Render Links
// --------------
//
diff --git a/article/nodes/cross_reference/cross_reference_view.js b/article/nodes/cross_reference/cross_reference_view.js
new file mode 100644
index 00000000..1ea2bf32
--- /dev/null
+++ b/article/nodes/cross_reference/cross_reference_view.js
@@ -0,0 +1,20 @@
+"use strict";
+
+var AnnotationView = require('../annotation/annotation_view');
+
+var CrossReferenceView = function(node, viewFactory) {
+ AnnotationView.call(this, node, viewFactory);
+ this.$el.addClass('cross-reference');
+};
+
+CrossReferenceView.Prototype = function() {
+ this.createElement = function() {
+ var el = document.createElement('a');
+ el.setAttribute('href', '');
+ return el;
+ };
+};
+CrossReferenceView.Prototype.prototype = AnnotationView.prototype;
+CrossReferenceView.prototype = new CrossReferenceView.Prototype();
+
+module.exports = CrossReferenceView;
diff --git a/article/nodes/cross_reference/index.js b/article/nodes/cross_reference/index.js
index 66279861..733cf974 100644
--- a/article/nodes/cross_reference/index.js
+++ b/article/nodes/cross_reference/index.js
@@ -1,5 +1,5 @@
module.exports = {
Model: require('./cross_reference.js'),
- View: require('../resource_reference/resource_reference_view.js')
+ View: require('./cross_reference_view.js')
};
diff --git a/article/nodes/custom_annotation/custom_annotation_view.js b/article/nodes/custom_annotation/custom_annotation_view.js
index 64304098..e873cd9d 100644
--- a/article/nodes/custom_annotation/custom_annotation_view.js
+++ b/article/nodes/custom_annotation/custom_annotation_view.js
@@ -8,7 +8,7 @@ CustomAnnotationView.Prototype = function() {
this.setClasses = function() {
AnnotationView.prototype.setClasses.call(this);
- this.$el.addClass(this.node.name);
+ this.$el.removeClass('custom_annotation').addClass(this.node.name);
};
};
diff --git a/article/nodes/footnote/footnote.js b/article/nodes/footnote/footnote.js
index 7fd4ac72..1ecd2c67 100644
--- a/article/nodes/footnote/footnote.js
+++ b/article/nodes/footnote/footnote.js
@@ -3,31 +3,20 @@
var Document = require('../../../substance/document');
var DocumentNode = Document.Node;
var Paragraph = require('../paragraph').Model;
-
+var Composite = Document.Composite;
var Footnote = function(node, document) {
- Paragraph.call(this, node, document);
+ Composite.call(this, node, document);
};
Footnote.type = {
"id": "footnote",
"parent": "paragraph",
"properties": {
- "label": "string"
- }
-};
-
-// This is used for the auto-generated docs
-// -----------------
-//
-
-Footnote.description = {
- "name": "Footnote",
- "remarks": [
- "A Footnote is basically a Paragraph with a label."
- ],
- "properties": {
- "label": "A string used as label",
+ "footnoteType": "string",
+ "specificUse": "string",
+ "label": "string",
+ "children": ["array", "string"]
}
};
@@ -54,6 +43,6 @@ Footnote.Prototype.prototype = Paragraph.prototype;
Footnote.prototype = new Footnote.Prototype();
Footnote.prototype.constructor = Footnote;
-DocumentNode.defineProperties(Footnote);
+DocumentNode.defineProperties(Footnote.prototype, ["children", "label", "footnoteType", "specificUse"]);
module.exports = Footnote;
diff --git a/article/nodes/footnote/footnote_view.js b/article/nodes/footnote/footnote_view.js
index d11367a9..5f025e4e 100644
--- a/article/nodes/footnote/footnote_view.js
+++ b/article/nodes/footnote/footnote_view.js
@@ -1,31 +1,20 @@
"use strict";
-var ParagraphView = require("../paragraph").View;
+var CompositeView = require("../composite/composite_view");
// Substance.Image.View
// ==========================================================================
var FootnoteView = function(node, viewFactory) {
- ParagraphView.call(this, node, viewFactory);
+ CompositeView.call(this, node, viewFactory);
};
FootnoteView.Prototype = function() {
- this.render = function() {
- ParagraphView.prototype.render.call(this);
-
- var labelEl = document.createElement('span');
- labelEl.classList.add('label');
- labelEl.innerHTML = this.node.label;
-
- this.el.insertBefore(labelEl, this.content);
-
- return this;
- };
};
-FootnoteView.Prototype.prototype = ParagraphView.prototype;
+FootnoteView.Prototype.prototype = CompositeView.prototype;
FootnoteView.prototype = new FootnoteView.Prototype();
module.exports = FootnoteView;
diff --git a/article/nodes/footnote_reference/footnote_reference.js b/article/nodes/footnote_reference/footnote_reference.js
new file mode 100644
index 00000000..faef8491
--- /dev/null
+++ b/article/nodes/footnote_reference/footnote_reference.js
@@ -0,0 +1,28 @@
+
+var Document = require('../../../substance/document');
+var Annotation = require('../annotation/annotation');
+var ResourceReference = require('../resource_reference/resource_reference');
+
+var FootnoteReference = function(node, doc) {
+ ResourceReference.call(this, node, doc);
+};
+
+FootnoteReference.type = {
+ id: "footnote_reference",
+ parent: "resource_reference",
+ properties: {
+ "target": "footnote"
+ }
+};
+
+FootnoteReference.Prototype = function() {};
+FootnoteReference.Prototype.prototype = ResourceReference.prototype;
+FootnoteReference.prototype = new FootnoteReference.Prototype();
+FootnoteReference.prototype.constructor = FootnoteReference;
+
+// Do not fragment this annotation
+FootnoteReference.fragmentation = Annotation.NEVER;
+
+Document.Node.defineProperties(FootnoteReference);
+
+module.exports = FootnoteReference;
diff --git a/article/nodes/footnote_reference/footnote_reference_view.js b/article/nodes/footnote_reference/footnote_reference_view.js
new file mode 100644
index 00000000..e787ef36
--- /dev/null
+++ b/article/nodes/footnote_reference/footnote_reference_view.js
@@ -0,0 +1,52 @@
+"use strict";
+
+var AnnotationView = require('../annotation/annotation_view');
+var $$ = require('../../../substance/application').$$;
+
+var FootnoteReferenceView = function(node, viewFactory) {
+ AnnotationView.call(this, node, viewFactory);
+ this.$el.addClass('footnote-reference');
+ this._expanded = false;
+};
+
+
+FootnoteReferenceView.Prototype = function() {
+
+ this.render = function() {
+ var footnote = this._getFootnote();
+ // this.el.innerHTML = formulaView.render().el.innerHTML;
+ this.el.innerHTML = "";
+ this.toggleEl = $$('a', {href: '#', html: footnote.properties.label});
+
+ $(this.toggleEl).on('click', this._onToggle.bind(this));
+ this.$el.append(this.toggleEl);
+ this.footnoteView = this._createView(footnote).render();
+ // HACK: some use xref with ref-type='fn' which will produce a different view class
+ this.footnoteView.$el.addClass('footnote');
+ if (this.node.properties.generated) {
+ this.$el.addClass('sm-generated');
+ }
+ this.$el.append(this.footnoteView.el);
+ };
+
+ this._onToggle = function(e) {
+ e.preventDefault();
+ this.$el.toggleClass('sm-expanded');
+ };
+
+ this._createView = function(node) {
+ var view = this.viewFactory.createView(node);
+ return view;
+ };
+
+ this._getFootnote = function() {
+ var node = this.node.document.get(this.node.target);
+ return node;
+ };
+};
+
+FootnoteReferenceView.Prototype.prototype = AnnotationView.prototype;
+FootnoteReferenceView.prototype = new FootnoteReferenceView.Prototype();
+
+module.exports = FootnoteReferenceView;
+
diff --git a/article/nodes/footnote_reference/index.js b/article/nodes/footnote_reference/index.js
new file mode 100644
index 00000000..0043da5a
--- /dev/null
+++ b/article/nodes/footnote_reference/index.js
@@ -0,0 +1,4 @@
+module.exports = {
+ Model: require('./footnote_reference.js'),
+ View: require('./footnote_reference_view.js')
+};
diff --git a/article/nodes/heading/heading.css b/article/nodes/heading/heading.css
index 8269f0fb..de388acc 100644
--- a/article/nodes/heading/heading.css
+++ b/article/nodes/heading/heading.css
@@ -14,7 +14,6 @@
.content-node.heading { }
.content-node.heading .content {
- color: rgba(0,0,0,0.8);
font-weight: 600;
line-height: 40px;
}
diff --git a/article/nodes/html_table/html_table.css b/article/nodes/html_table/html_table.css
index 4240c5f7..ec8b8ad2 100644
--- a/article/nodes/html_table/html_table.css
+++ b/article/nodes/html_table/html_table.css
@@ -19,8 +19,8 @@
position: relative;
border-collapse: collapse;
border-spacing: 0;
- margin-bottom: 20px;
margin: 0 auto;
+ margin-bottom: 20px;
}
.lens-article .content-node.html-table thead tr {
diff --git a/article/nodes/index.js b/article/nodes/index.js
index 6d897b20..38a74a25 100644
--- a/article/nodes/index.js
+++ b/article/nodes/index.js
@@ -22,6 +22,7 @@ module.exports = {
"citation_reference": require("./citation_reference"),
"definition_reference": require("./definition_reference"),
"cross_reference": require("./cross_reference"),
+ "footnote_reference": require("./footnote_reference"),
"publication_info": require("./publication_info"),
/* Annotation'ish content types */
"link": require("./link"),
@@ -48,5 +49,5 @@ module.exports = {
"codeblock": require("./codeblock"),
"affiliation": require("./_affiliation"),
"footnote": require("./footnote"),
- "quote": require("./quote")
+ "quote": require("./quote"),
};
diff --git a/article/nodes/node/node_view.js b/article/nodes/node/node_view.js
index e80ea21b..2c23cab9 100644
--- a/article/nodes/node/node_view.js
+++ b/article/nodes/node/node_view.js
@@ -58,8 +58,7 @@ NodeView.Prototype = function() {
};
this.renderAnnotatedText = function(path, el) {
- var property = this.node.document.resolve(path);
- var view = TextPropertyView.renderAnnotatedText(this.node.document, property, el, this.viewFactory);
+ var view = TextPropertyView.renderAnnotatedText(this.node.document, path, el, this.viewFactory);
return view;
};
diff --git a/article/nodes/paragraph/paragraph.css b/article/nodes/paragraph/paragraph.css
index 6ce5306e..2677e100 100644
--- a/article/nodes/paragraph/paragraph.css
+++ b/article/nodes/paragraph/paragraph.css
@@ -18,7 +18,7 @@
display:block;
}
-.content-node.paragraph .content-node.text div {
+.content-node.paragraph > .content-node.text > div {
display:inline;
width: auto;
}
diff --git a/article/nodes/publication_info/publication_info.css b/article/nodes/publication_info/publication_info.css
index 13835dac..9d9ef391 100644
--- a/article/nodes/publication_info/publication_info.css
+++ b/article/nodes/publication_info/publication_info.css
@@ -1,8 +1,8 @@
/* Publication Info */
.lens-article .content-node.publication-info {
- font-size: 14px;
color: #333;
+ font-size: 16px;
}
.lens-article .content-node.publication-info table {
@@ -26,30 +26,19 @@
margin-left: 140px;
}
-.lens-article .content-node.publication-info .dates {
- /*font-size: 14px;*/
-}
-
.article .resources .nodes > .content-node.publication-info > .content {
border: none;
padding: 20px;
}
-.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] {
- font-size: 14px;
-}
-
-.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] .heading.level-3 {
- padding-top: 10px;
-}
-
.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] .heading.level-3 .content {
- font-size: 14px;
- font-weight: normal;
- font-family: "Source Sans Pro";
- color: #999;
+ font-size: 20px;
+ margin-top: 12px;
+ /* The 'content-node paragraph' below has a padding top making the space between the heading and text 22px rather than 12. */
+ /* margin-bottom: 12px; */
}
.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] .content-node {
padding-top: 10px;
}
+
diff --git a/article/nodes/publication_info/publication_info.js b/article/nodes/publication_info/publication_info.js
index b152804d..ab190a84 100644
--- a/article/nodes/publication_info/publication_info.js
+++ b/article/nodes/publication_info/publication_info.js
@@ -22,7 +22,10 @@ PublicationInfo.type = {
"links": ["array", "objects"],
"doi": "string",
"related_article": "string",
- "article_info": "paragraph"
+ "article_info": "paragraph",
+ // optional
+ "subject_link": "string",
+ "article_type_link": "string"
}
};
@@ -86,6 +89,22 @@ PublicationInfo.Prototype = function() {
return this.document.get("articleinfo");
};
+ this.getSubjectLinks = function() {
+ return this.subjects.map(function(subject) {
+ return {
+ name: subject,
+ url: this.subject_link + '/' + subject.replace(/ /g, '-').toLowerCase()
+ }
+ }.bind(this))
+ }
+
+ this.getArticleTypeLink = function() {
+ return {
+ name: this.article_type,
+ url: this.article_type_link + '/' + this.article_type.replace(/ /g, '-').toLowerCase()
+ }
+ }
+
};
PublicationInfo.Prototype.prototype = Document.Node.prototype;
diff --git a/article/nodes/text/text_property_view.js b/article/nodes/text/text_property_view.js
index f790d824..26742e55 100644
--- a/article/nodes/text/text_property_view.js
+++ b/article/nodes/text/text_property_view.js
@@ -34,7 +34,7 @@ TextPropertyView.Prototype = function() {
this.render = function() {
this.el.innerHTML = "";
- TextPropertyView.renderAnnotatedText(this.document, this.property, this.el, this.viewFactory);
+ TextPropertyView.renderAnnotatedText(this.document, this.path, this.el, this.viewFactory);
return this;
};
@@ -81,10 +81,10 @@ TextPropertyView.Prototype = function() {
TextPropertyView.Prototype.prototype = View.prototype;
TextPropertyView.prototype = new TextPropertyView.Prototype();
-TextPropertyView.renderAnnotatedText = function(doc, property, el, viewFactory) {
+TextPropertyView.renderAnnotatedText = function(doc, path, el, viewFactory) {
var fragment = window.document.createDocumentFragment();
- var text = property.get();
- var annotations = doc.getIndex("annotations").get(property.path);
+ var text = doc.get(path);
+ var annotations = doc.getIndex("annotations").get(path);
// this splits the text and annotations into smaller pieces
// which is necessary to generate proper HTML.
var annotationViews = [];
diff --git a/article/nodes/video/video.js b/article/nodes/video/video.js
index 29775c2c..55cc16d6 100644
--- a/article/nodes/video/video.js
+++ b/article/nodes/video/video.js
@@ -58,10 +58,10 @@ Video.example = {
"id": "video_1",
"type": "video",
"label": "Video 1.",
- "url": "http://cdn.elifesciences.org/video/eLifeLensIntro2.mp4",
- "url_webm": "http://cdn.elifesciences.org/video/eLifeLensIntro2.webm",
- "url_ogv": "http://cdn.elifesciences.org/video/eLifeLensIntro2.ogv",
- "poster": "http://cdn.elifesciences.org/video/eLifeLensIntro2.png",
+ "url": "https://cdn.elifesciences.org/video/eLifeLensIntro2.mp4",
+ "url_webm": "https://cdn.elifesciences.org/video/eLifeLensIntro2.webm",
+ "url_ogv": "https://cdn.elifesciences.org/video/eLifeLensIntro2.ogv",
+ "poster": "https://cdn.elifesciences.org/video/eLifeLensIntro2.png",
// "doi": "http://dx.doi.org/10.7554/Fake.doi.003",
"caption": "caption_25"
};
diff --git a/converter/elife_converter.js b/converter/elife_converter.js
index ce9e7b9f..b02e0127 100644
--- a/converter/elife_converter.js
+++ b/converter/elife_converter.js
@@ -3,7 +3,7 @@
var util = require("../substance/util");
var _ = require("underscore");
-var LensConverter = require('lens/converter');
+var LensConverter = require('./lens_converter');
var ElifeConverter = function(options) {
LensConverter.call(this, options);
@@ -13,6 +13,12 @@ ElifeConverter.Prototype = function() {
var __super__ = LensConverter.prototype;
+ // Fix to focus on videos from left reading pane to the right figures pane
+ if (!("video" in __super__._refTypeMapping))
+ {
+ __super__._refTypeMapping["video"] = "figure_reference";
+ }
+
this.test = function(xmlDoc, documentUrl) {
var publisherName = xmlDoc.querySelector("publisher-name").textContent;
return publisherName === "eLife Sciences Publications, Ltd";
@@ -34,51 +40,23 @@ ElifeConverter.Prototype = function() {
var doc = state.doc;
var heading, body;
- // Decision letter (if available)
- // -----------
-
- var articleCommentary = article.querySelector("#SA1");
- if (articleCommentary) {
- heading = {
- id: state.nextId("heading"),
- type: "heading",
- level: 1,
- content: "Article Commentary"
- };
- doc.create(heading);
- nodes.push(heading);
-
- heading = {
- id: state.nextId("heading"),
- type: "heading",
- level: 2,
- content: "Decision letter"
- };
- doc.create(heading);
- nodes.push(heading);
-
- body = articleCommentary.querySelector("body");
- nodes = nodes.concat(this.bodyNodes(state, util.dom.getChildren(body)));
- }
-
- // Author response
+ // Decision letter and author response sub articles (if available)
// -----------
-
- var authorResponse = article.querySelector("#SA2");
- if (authorResponse) {
-
- heading = {
- id: state.nextId("heading"),
- type: "heading",
- level: 2,
- content: "Author response"
- };
- doc.create(heading);
- nodes.push(heading);
-
- body = authorResponse.querySelector("body");
- nodes = nodes.concat(this.bodyNodes(state, util.dom.getChildren(body)));
- }
+ var subArticles = article.querySelectorAll("sub-article");
+ _.each(subArticles, function(subArticle) {
+ var subArticleTitle = subArticle.querySelector("title-group article-title").textContent;
+ heading = {
+ id: state.nextId("heading"),
+ type: "heading",
+ level: 1,
+ content: subArticleTitle
+ };
+ doc.create(heading);
+ nodes.push(heading);
+
+ body = subArticle.querySelector("body");
+ nodes = nodes.concat(this.bodyNodes(state, util.dom.getChildren(body)));
+ }.bind(this));
// Show them off
// ----------
@@ -88,23 +66,6 @@ ElifeConverter.Prototype = function() {
}
};
- this.enhanceCover = function(state, node, element) {
- var category;
- var dispChannel = element.querySelector("subj-group[subj-group-type=display-channel] subject").textContent;
- try {
- category = element.querySelector("subj-group[subj-group-type=heading] subject").textContent;
- } catch(err) {
- category = null;
- }
-
- node.breadcrumbs = [
- { name: "eLife", url: "http://elifesciences.org/", image: "http://lens.elifesciences.org/lens-elife/styles/elife.png" },
- { name: dispChannel, url: "http://elifesciences.org/category/"+dispChannel.replace(/ /g, '-').toLowerCase() },
- ];
-
- if (category) node.breadcrumbs.push( { name: category, url: "http://elifesciences.org/category/"+category.replace(/ /g, '-').toLowerCase() } );
- };
-
// Resolves figure url
// --------
//
@@ -116,7 +77,7 @@ ElifeConverter.Prototype = function() {
};
- // Example url to JPG: http://cdn.elifesciences.org/elife-articles/00768/svg/elife00768f001.jpg
+ // Example url to JPG: https://cdn.elifesciences.org/elife-articles/00768/svg/elife00768f001.jpg
this.resolveURL = function(state, url) {
// Use absolute URL
if (url.match(/http:\/\//)) return url;
@@ -135,9 +96,9 @@ ElifeConverter.Prototype = function() {
} else if (!(url.match(/\.gif$/g))) {
url = url+'.jpg'
}
-
+
return [
- "http://publishing-cdn.elifesciences.org/",
+ "https://cdn.elifesciences.org/articles/",
state.doc.id,
"/",
url
@@ -151,7 +112,7 @@ ElifeConverter.Prototype = function() {
return [baseURL, node.url].join('');
} else {
node.url = [
- "http://publishing-cdn.elifesciences.org/",
+ "https://cdn.elifesciences.org/articles/",
state.doc.id,
"/",
node.url
@@ -219,13 +180,13 @@ ElifeConverter.Prototype = function() {
if (pdfURI) {
var pdfLink = [
- "http://publishing-cdn.elifesciences.org/",
+ "https://cdn.elifesciences.org/articles/",
state.doc.id,
"/",
pdfURI ? pdfURI.getAttribute("xlink:href") : "#"
].join('');
}
-
+
// Version number from the PDF href, default to 1
var match = null;
if (pdfURI) {
@@ -247,7 +208,7 @@ ElifeConverter.Prototype = function() {
}
links.push({
- url: "https://s3.amazonaws.com/elife-publishing-cdn/"+state.doc.id+"/elife-"+state.doc.id+"-v"+version+".xml",
+ url: "https://cdn.elifesciences.org/articles/"+state.doc.id+"/elife-"+state.doc.id+"-v"+version+".xml",
name: "Source XML",
type: "xml"
});
@@ -266,6 +227,9 @@ ElifeConverter.Prototype = function() {
publicationInfo.article_type = articleType ? articleType.textContent : "";
publicationInfo.links = links;
+ publicationInfo.subject_link = 'https://elifesciences.org/category'
+ publicationInfo.article_type_link = 'https://elifesciences.org/category'
+
if (publicationInfo.related_article) publicationInfo.related_article = "http://dx.doi.org/" + publicationInfo.related_article;
};
@@ -275,7 +239,7 @@ ElifeConverter.Prototype = function() {
return [baseURL, node.url].join('');
} else {
node.url = [
- "http://publishing-cdn.elifesciences.org/",
+ "https://cdn.elifesciences.org/articles/",
state.doc.id,
"/",
node.url
@@ -283,16 +247,70 @@ ElifeConverter.Prototype = function() {
}
};
+ this.enhanceTable = function(state, tableNode, tableWrap) {
+ this.mapCitations(state);
+ tableNode.content = this.enhanceHTML(tableNode.content);
+ }
+
+ this.mapCitations = function (state)
+ {
+ // Map citation id to their citation reference for use in HTML replacements
+ this.citationCitationReferenceMap = [];
+ var citationSourceIdMap = [];
+ var doc = state.doc;
+ _.each(doc.nodes, function(node) {
+ if(node.type == "citation")
+ {
+ citationSourceIdMap[node.source_id] = node.id;
+ }
+ }.bind(this));
+ var annotations = state.annotations;
+ _.each(annotations, function(anno) {
+ if(anno.type == "citation_reference")
+ {
+ this.citationCitationReferenceMap[anno.target] = anno.id;
+ }
+ }.bind(this));
+ }
+
+ this.enhanceHTML = function(html) {
+ html = html.replace(/<(\/)?bold>/g, "<$1strong>");
+ html = html.replace(/<(\/)?italic>/g, "<$1em>");
+ // Table colours
+ html = html.replace(/
]*)>/g, " | ");
+ // named-content span
+ html = html.replace(/]*)>([^<]*)<\/named-content>/g, "$3");
+ // Hacky way to convert references inside tables
+ if(this.citationCitationReferenceMap)
+ {
+ var xref_pattern = /([^<]*)<\/xref>/g;
+ var matches = html.match(xref_pattern);
+ _.each(matches, function(match) {
+ var rid = match.replace(xref_pattern, "$1");
+ var content = match.replace(xref_pattern, "$2");
+ var citation_reference = this.citationCitationReferenceMap[rid];
+ var replace_tag = ''+content+'';
+ html = html.replace(match, replace_tag);
+ }.bind(this));
+ }
+ return html;
+ };
+
this.enhanceVideo = function(state, node, element) {
+ var id = element.getAttribute("id");
var href = element.getAttribute("xlink:href").split(".");
var name = href[0];
- node.url = "http://api.elifesciences.org/v2/articles/"+state.doc.id+"/media/file/"+name+".mp4";
- node.url_ogv = "http://api.elifesciences.org/v2/articles/"+state.doc.id+"/media/file//"+name+".ogv";
- node.url_webm = "http://api.elifesciences.org/v2/articles/"+state.doc.id+"/media/file//"+name+".webm";
- node.poster = "http://api.elifesciences.org/v2/articles/"+state.doc.id+"/media/file/"+name+".jpg";
+
+ // set attributes from previously populated data in video_data
+ if(typeof video_data !== 'undefined' && id in video_data) {
+ node.url = video_data[id]['mp4_href'];
+ node.url_ogv = video_data[id]['ogv_href'];
+ node.url_webm = video_data[id]['webm_href'];
+ node.poster = video_data[id]['jpg_href'];
+ }
};
- // Example url to JPG: http://cdn.elifesciences.org/elife-articles/00768/svg/elife00768f001.jpg
+ // Example url to JPG: https://cdn.elifesciences.org/elife-articles/00768/svg/elife00768f001.jpg
this.resolveURL = function(state, url) {
// Use absolute URL
if (url.match(/http:\/\//)) return url;
@@ -304,16 +322,16 @@ ElifeConverter.Prototype = function() {
return [baseURL, url].join('');
} else {
// Use special URL resolving for production articles
-
+
// File extension support
if (url.match(/\.tif$/g)) {
url = url.replace(/\.tif$/g, '.jpg')
} else if (!(url.match(/\.gif$/g))) {
url = url+'.jpg'
}
-
+
return [
- "http://publishing-cdn.elifesciences.org/",
+ "https://cdn.elifesciences.org/articles/",
state.doc.id,
"/",
url
@@ -334,15 +352,29 @@ ElifeConverter.Prototype = function() {
}
};
+ this.back = function(state, back) {
+ var appGroups = back.querySelectorAll('app-group');
+
+ if (appGroups && appGroups.length > 0) {
+ _.each(appGroups, function(appGroup) {
+ this.appGroup(state, appGroup);
+ }.bind(this));
+ }
+ };
+
this.showNode = function(state, node) {
switch(node.type) {
// Boxes go into the figures view if these conditions are met
// 1. box has a label (e.g. elife 00288)
+ // Disable it July 2017: cross reference links do not work if box reference
+ // in the content panel tries to link to the figures panel
+ /*
case "box":
if (node.label) {
state.doc.show("figures", node.id);
}
break;
+ */
default:
__super__.showNode.apply(this, arguments);
}
diff --git a/converter/lens_converter.js b/converter/lens_converter.js
index af23dcd4..0e1138c1 100644
--- a/converter/lens_converter.js
+++ b/converter/lens_converter.js
@@ -19,6 +19,9 @@ NlmToLensConverter.Prototype = function() {
"sub": "subscript",
"sup": "superscript",
"sc": "custom_annotation",
+ "roman": "custom_annotation",
+ "sans-serif": "custom_annotation",
+ "styled-content": "custom_annotation",
"underline": "underline",
"ext-link": "link",
"xref": "",
@@ -28,6 +31,10 @@ NlmToLensConverter.Prototype = function() {
"uri": "link"
};
+ this._inlineNodeTypes = {
+ "fn": true,
+ };
+
// mapping from xref.refType to node type
this._refTypeMapping = {
"bibr": "citation_reference",
@@ -36,6 +43,7 @@ NlmToLensConverter.Prototype = function() {
"supplementary-material": "figure_reference",
"other": "figure_reference",
"list": "definition_reference",
+ "fn": "footnote_reference",
};
// mapping of contrib type to human readable names
@@ -73,6 +81,10 @@ NlmToLensConverter.Prototype = function() {
return this._annotationTypes[type] !== undefined;
};
+ this.isInlineNode = function(type) {
+ return this._inlineNodeTypes[type] !== undefined;
+ };
+
this.isParagraphish = function(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var el = node.childNodes[i];
@@ -99,7 +111,7 @@ NlmToLensConverter.Prototype = function() {
if (givenNamesEl) names.push(givenNamesEl.textContent);
if (surnameEl) names.push(surnameEl.textContent);
- if (suffix) return [names.join(" "), suffix.textContent].join(", ");
+ if (suffix && suffix.textContent.trim() !== "") return [names.join(" "), suffix.textContent].join(", ");
return names.join(" ");
};
@@ -176,7 +188,7 @@ NlmToLensConverter.Prototype = function() {
// Overridden to create a Lens Article instance
this.createDocument = function() {
-
+
var doc = new Article();
return doc;
};
@@ -225,6 +237,9 @@ NlmToLensConverter.Prototype = function() {
// Article information
var articleInfo = this.extractArticleInfo(state, article);
+ // Funding information
+ var fundingInfo = this.extractFundingInfo(state, article);
+
// Create PublicationInfo node
// ---------------
@@ -236,6 +251,7 @@ NlmToLensConverter.Prototype = function() {
"related_article": relatedArticle ? relatedArticle.getAttribute("xlink:href") : "",
"doi": articleDOI ? articleDOI.textContent : "",
"article_info": articleInfo.id,
+ "funding_info": fundingInfo,
// TODO: 'article_type' should not be optional; we need to find a good default implementation
"article_type": "",
// Optional fields not covered by the default implementation
@@ -288,7 +304,7 @@ NlmToLensConverter.Prototype = function() {
nodes = nodes.concat(this.extractAcknowledgements(state, article));
// License and Copyright
nodes = nodes.concat(this.extractCopyrightAndLicense(state, article));
- // Notes (Footnotes + Author notes)
+ // Notes ( elements)
nodes = nodes.concat(this.extractNotes(state, article));
articleInfo.children = nodes;
@@ -297,6 +313,18 @@ NlmToLensConverter.Prototype = function() {
return articleInfo;
};
+ this.extractFundingInfo = function(state, article) {
+ var fundingInfo = [];
+ var fundingStatementEls = article.querySelectorAll("funding-statement");
+ if (fundingStatementEls.length > 0){
+ for (var i = 0; i < fundingStatementEls.length; i++) {
+ fundingInfo.push(this.annotatedText(state, fundingStatementEls[i], ["publication_info", "funding_info", i]));
+ }
+ }
+
+ return fundingInfo;
+ };
+
// Get reviewing editor
// --------------
// TODO: it is possible to have multiple editors. This does only show the first one
@@ -403,6 +431,7 @@ NlmToLensConverter.Prototype = function() {
"level" : 3,
"content" : title ? this.capitalized(title.textContent.toLowerCase(), "all") : "Acknowledgements"
};
+
doc.create(header);
nodes.push(header.id);
@@ -410,6 +439,7 @@ NlmToLensConverter.Prototype = function() {
var pars = this.bodyNodes(state, util.dom.getChildren(ack), {
ignore: ["title"]
});
+
_.each(pars, function(par) {
nodes.push(par.id);
});
@@ -420,14 +450,12 @@ NlmToLensConverter.Prototype = function() {
};
//
- // Extracts footnotes that should be shown in article info
+ // Extracts notes that should be shown in article info
// ------------------------------------------
//
- // Needs to be overwritten in configuration
-
- this.extractNotes = function(/*state, article*/) {
- var nodes = [];
- return nodes;
+ this.extractNotes = function(state, article) {
+ /* jshint unused:false */
+ return [];
};
// Can be overridden by custom converter to ignore values.
@@ -439,7 +467,7 @@ NlmToLensConverter.Prototype = function() {
var nodeIds = [];
var doc = state.doc;
- var customMetaEls = article.querySelectorAll('article-meta-group custom-meta');
+ var customMetaEls = article.querySelectorAll('article-meta custom-meta');
if (customMetaEls.length === 0) return nodeIds;
for (var i = 0; i < customMetaEls.length; i++) {
@@ -595,11 +623,19 @@ NlmToLensConverter.Prototype = function() {
this.affiliation = function(state, aff) {
var doc = state.doc;
- var institution = aff.querySelector("institution");
+ var department = aff.querySelector("institution[content-type=dept]");
+ if (department) {
+ var institution = aff.querySelector("institution:not([content-type=dept])");
+ } else {
+ var department = aff.querySelector("addr-line named-content[content-type=department]");
+ var institution = aff.querySelector("institution");
+ }
var country = aff.querySelector("country");
var label = aff.querySelector("label");
- var department = aff.querySelector("addr-line named-content[content-type=department]");
+
var city = aff.querySelector("addr-line named-content[content-type=city]");
+ // TODO: there are a lot more elements which can have this.
+ var specific_use = aff.getAttribute('specific-use');
// TODO: this is a potential place for implementing a catch-bin
// For that, iterate all children elements and fill into properties as needed or add content to the catch-bin
@@ -612,7 +648,8 @@ NlmToLensConverter.Prototype = function() {
department: department ? department.textContent : null,
city: city ? city.textContent : null,
institution: institution ? institution.textContent : null,
- country: country ? country.textContent: null
+ country: country ? country.textContent: null,
+ specific_use: specific_use || null
};
doc.create(affiliationNode);
};
@@ -771,7 +808,20 @@ NlmToLensConverter.Prototype = function() {
//
//
// and we only want to display the first text node, excluding the funder id
- var fundingSourceName = fundingSource.childNodes[0].textContent;
+ // or this
+ //
+ // They can also look like this
+ //
+ //
+ //
+ // http://dx.doi.org/10.13039/100005156
+ // Alexander von Humboldt-Stiftung
+ //
+ //
+ // Then we take the institution element
+
+ var institution = fundingSource.querySelector('institution')
+ var fundingSourceName = institution ? institution.textContent : fundingSource.childNodes[0].textContent;
contribNode.fundings.push([fundingSourceName, awardId].join(''));
} else if (xref.getAttribute("ref-type") === "corresp") {
var correspId = xref.getAttribute("rid");
@@ -791,7 +841,11 @@ NlmToLensConverter.Prototype = function() {
var fnType = fnElem.getAttribute("fn-type");
switch (fnType) {
case "con":
- contribNode.contribution = fnElem.textContent;
+ if (fnElem.getAttribute("id").indexOf("equal-contrib")>=0) {
+ equalContribs = this._getEqualContribs(state, contrib, fnElem.getAttribute("id"));
+ } else {
+ contribNode.contribution = fnElem.textContent;
+ }
break;
case "conflict":
compInterests.push(fnElem.textContent.trim());
@@ -949,6 +1003,15 @@ NlmToLensConverter.Prototype = function() {
this.extractFigures(state, article);
+ // catch all unhandled foot-notes
+ this.extractFootNotes(state, article);
+
+ // Extract back element, if it exists
+ var back = article.querySelector("back");
+ if (back){
+ this.back(state,back);
+ }
+
this.enhanceArticle(state, article);
};
@@ -1029,8 +1092,10 @@ NlmToLensConverter.Prototype = function() {
this.extractFigures = function(state, xmlDoc) {
// Globally query all figure-ish content, , , ,
// mimetype="video"
- var body = xmlDoc.querySelector("body");
- var figureElements = body.querySelectorAll("fig, table-wrap, supplementary-material, media[mimetype=video]");
+
+ // NOTE: We previously only considered figures within but since
+ // appendices can also have figures we now use a gobal selector.
+ var figureElements = xmlDoc.querySelectorAll("fig, table-wrap, supplementary-material, media[mimetype=video]");
var nodes = [];
for (var i = 0; i < figureElements.length; i++) {
var figEl = figureElements[i];
@@ -1054,6 +1119,17 @@ NlmToLensConverter.Prototype = function() {
this.show(state, nodes);
};
+ // Catch-all implementation for footnotes that have not been
+ // converted yet.
+ this.extractFootNotes = function(state, article) {
+ var fnEls = article.querySelectorAll('fn');
+ for (var i = 0; i < fnEls.length; i++) {
+ var fnEl = fnEls[i];
+ if (fnEl.__converted__) continue;
+ this.footnote(state, fnEl);
+ }
+ };
+
this.extractCitations = function(state, xmlDoc) {
var refList = xmlDoc.querySelector("ref-list");
if (refList) {
@@ -1128,6 +1204,8 @@ NlmToLensConverter.Prototype = function() {
}, this);
};
+ // TODO: abstract should be a dedicated node
+ // as it can have some extra information in JATS, such as specific-use
this.abstract = function(state, abs) {
var doc = state.doc;
var nodes = [];
@@ -1161,14 +1239,7 @@ NlmToLensConverter.Prototype = function() {
this.body = function(state, body) {
var doc = state.doc;
- var heading = {
- id: state.nextId("heading"),
- type: "heading",
- level: 1,
- content: "Main Text"
- };
- doc.create(heading);
- var nodes = [heading].concat(this.bodyNodes(state, util.dom.getChildren(body)));
+ var nodes = this.bodyNodes(state, util.dom.getChildren(body));
if (nodes.length > 0) {
this.show(state, nodes);
}
@@ -1207,7 +1278,7 @@ NlmToLensConverter.Prototype = function() {
node = this.ignoredNode(state, child, type);
if (node) nodes.push(node);
} else {
- console.error("Node not yet supported as top-level node: " + type);
+ console.error("Node not supported as block-level element: " + type +"\n"+child.outerHTML);
}
}
return nodes;
@@ -1240,9 +1311,10 @@ NlmToLensConverter.Prototype = function() {
this._bodyNodes["comment"] = function(state, child) {
return this.comment(state, child);
};
- this._bodyNodes["fig"] = function(state, child) {
- return this.figure(state, child);
- };
+ // Disable fig as a body node, otherwise the order of nodes in the Figures tab can be incorrect
+ //this._bodyNodes["fig"] = function(state, child) {
+ // return this.figure(state, child);
+ //};
// Overwirte in specific converter
this.ignoredNode = function(/*state, node, type*/) {
@@ -1258,11 +1330,13 @@ NlmToLensConverter.Prototype = function() {
// Assuming that there are no nested elements
var childNodes = this.bodyNodes(state, util.dom.getChildren(box));
var boxId = state.nextId("box");
+ // Optional heading label
+ var label = this.selectDirectChildren(box, "label")[0];
var boxNode = {
"type": "box",
"id": boxId,
"source_id": box.getAttribute("id"),
- "label": "",
+ "label": label ? label.textContent : "",
"children": _.pluck(childNodes, 'id')
};
doc.create(boxNode);
@@ -1283,9 +1357,9 @@ NlmToLensConverter.Prototype = function() {
};
doc.create(quoteNode);
return quoteNode;
- };
+ };
- this.datasets = function(state, datasets) {
+ this.datasets = function(state, datasets) {
var nodes = [];
for (var i=0;i element
@@ -1446,8 +1521,15 @@ NlmToLensConverter.Prototype = function() {
// ignore some elements
if (this.ignoredParagraphElements[type]) continue;
+ // paragraph block-types such as disp-formula
+ // i.e they are allowed within a paragraph, but
+ // we pull them out on the top level
+ if (this.acceptedParagraphElements[type]) {
+ blocks.push(_.extend({node: child}, this.acceptedParagraphElements[type]));
+ }
// paragraph elements
- if (type === "text" || this.isAnnotation(type) || this.inlineParagraphElements[type]) {
+ //if (type === "text" || this.isAnnotation(type) || this.inlineParagraphElements[type]) {
+ else {
if (lastType !== "paragraph") {
blocks.push({ handler: "paragraph", nodes: [] });
lastType = "paragraph";
@@ -1455,10 +1537,7 @@ NlmToLensConverter.Prototype = function() {
_.last(blocks).nodes.push(child);
continue;
}
- // other elements are treated as single blocks
- else if (this.acceptedParagraphElements[type]) {
- blocks.push(_.extend({node: child}, this.acceptedParagraphElements[type]));
- }
+
lastType = type;
}
return blocks;
@@ -1491,6 +1570,11 @@ NlmToLensConverter.Prototype = function() {
return nodes;
};
+ // DEPRECATED: using this handler for elements is
+ // deprecated, as in JATS can contain certain block-level
+ // elements. Better use this.paragraphGroup in cases where you
+ // convert elements.
+ // TODO: we should refactor this and make it a 'private' helper
this.paragraph = function(state, children) {
var doc = state.doc;
@@ -1511,7 +1595,7 @@ NlmToLensConverter.Prototype = function() {
var type = util.dom.getNodeType(child);
// annotated text node
- if (type === "text" || this.isAnnotation(type)) {
+ if (type === "text" || this.isAnnotation(type) || this.isInlineNode(type)) {
var textNode = {
id: state.nextId("text"),
type: "text",
@@ -1526,7 +1610,13 @@ NlmToLensConverter.Prototype = function() {
// In that case, the iterator will still have more elements
// and the loop is continued
// Before descending, we reset the iterator to provide the current element again.
- var annotatedText = this._annotatedText(state, iterator.back(), { offset: 0, breakOnUnknown: true });
+ // TODO: We have disabled the described behavior as it seems
+ // worse to break automatically on unknown inline tags,
+ // than to render plain text, as it results in data loss.
+ // If you find a situation where you want to flatten structure
+ // found within a paragraph, use this.acceptedParagraphElements instead
+ // which is used in a preparation step before converting paragraphs.
+ var annotatedText = this._annotatedText(state, iterator.back(), { offset: 0, breakOnUnknown: false });
// Ignore empty paragraphs
if (annotatedText.length > 0) {
@@ -1538,7 +1628,6 @@ NlmToLensConverter.Prototype = function() {
// popping the stack
state.stack.pop();
}
-
// inline image node
else if (type === "inline-graphic") {
var url = child.getAttribute("xlink:href");
@@ -1599,6 +1688,8 @@ NlmToLensConverter.Prototype = function() {
var listItems = list.querySelectorAll("list-item");
for (var i = 0; i < listItems.length; i++) {
var listItem = listItems[i];
+ // Only consider direct children
+ if (listItem.parentNode !== list) continue;
// Note: we do not care much about what is served as items
// However, we do not have complex nodes on paragraph level
// They will be extract as sibling items
@@ -1825,9 +1916,11 @@ NlmToLensConverter.Prototype = function() {
};
// Note: using a DOM div element to create HTML
- var table = tableWrap.querySelector("table");
+ var table = tableWrap.querySelectorAll("table");
if (table) {
- tableNode.content = this.toHtml(table);
+ for (var i = 0; i < table.length; i++) {
+ tableNode.content += this.toHtml(table[i]);
+ }
}
this.extractTableCaption(state, tableNode, tableWrap);
@@ -1913,6 +2006,33 @@ NlmToLensConverter.Prototype = function() {
return formulaNode;
};
+ this.footnote = function(state, footnoteElement) {
+ var doc = state.doc;
+ var footnote = {
+ type: 'footnote',
+ id: state.nextId('fn'),
+ source_id: footnoteElement.getAttribute("id"),
+ label: '',
+ children: []
+ };
+ var children = footnoteElement.children;
+ var i = 0;
+ if (children[i].tagName.toLowerCase() === 'label') {
+ footnote.label = this.annotatedText(state, children[i], [footnote.id, 'label']);
+ i++;
+ }
+ footnote.children = [];
+ for (; i 0) {
+ _.each(appGroups, function(appGroup) {
+ this.appGroup(state, appGroup);
+ }.bind(this));
+ } else {
+ // HACK: We treat element as app-group, sine there
+ // are docs that wrongly put elements into the back
+ // element directly.
+ this.appGroup(state, back);
+ }
+ };
+
+ this.appGroup = function(state, appGroup) {
+ var apps = appGroup.querySelectorAll('app');
+ var doc = state.doc;
+ var title = appGroup.querySelector('title');
+ if (!title) {
+ console.error("FIXME: every app should have a title", this.toHtml(title));
+ }
+
+ var headingId =state.nextId("heading");
+ // Insert top level element for Appendix
+ var heading = doc.create({
+ "type" : "heading",
+ "id" : headingId,
+ "level" : 1,
+ "content" : "Appendices"
+ });
+
+ this.show(state, [heading]);
+ _.each(apps, function(app) {
+ state.sectionLevel = 2;
+ this.app(state, app);
+ }.bind(this));
+ };
+
+ this.app = function(state, app) {
+ var doc = state.doc;
+ var nodes = [];
+ var title = app.querySelector('title');
+ if (!title) {
+ console.error("FIXME: every app should have a title", this.toHtml(title));
+ }
+
+ var headingId = state.nextId("heading");
+ var heading = {
+ "type" : "heading",
+ "id" : headingId,
+ "level" : 2,
+ "content": title ? this.annotatedText(state, title, [headingId, "content"]) : ""
+ };
+ var headingNode = doc.create(heading);
+ nodes.push(headingNode);
+
+ // There may be multiple paragraphs per ack element
+ var pars = this.bodyNodes(state, util.dom.getChildren(app), {
+ ignore: ["title", "label", "ref-list"]
+ });
+ _.each(pars, function(par) {
+ nodes.push(par);
+ });
+ this.show(state, nodes);
};
+
+
// Annotations
// -----------
@@ -2128,6 +2312,8 @@ NlmToLensConverter.Prototype = function() {
} else if (type === 'inline-formula') {
var formula = this.formula(state, el, "inline");
anno.target = formula.id;
+ } else if (anno.type === 'custom_annotation') {
+ anno.name = type;
}
};
@@ -2139,6 +2325,41 @@ NlmToLensConverter.Prototype = function() {
if (sourceId) anno.target = sourceId;
};
+ this.createInlineNode = function(state, el, start) {
+ var inlineNode = {
+ type: "inline-node",
+ path: _.last(state.stack).path,
+ range: [start, start+1],
+ };
+
+ this.addInlineNodeData(state, inlineNode, el);
+ this.enhanceInlineNodeData(state, inlineNode, el);
+
+ // assign an id after the type has been extracted to be able to create typed ids
+ inlineNode.id = state.nextId(inlineNode.type);
+
+ state.annotations.push(inlineNode);
+ };
+
+ this.addInlineNodeData = function(state, inlineNode, el) {
+ /*jshint unused: false*/
+ var tagName = el.tagName.toLowerCase();
+ switch(tagName) {
+ case 'fn':
+ // when we hit a inline, we will create a footnote-reference
+ var footnote = this.footnote(state, el);
+ inlineNode.type = 'footnote_reference';
+ inlineNode.target = footnote.id;
+ // We generate footnote references if we find an inline fn element
+ inlineNode.generated = true;
+ break;
+ }
+ };
+
+ this.enhanceInlineNodeData = function(state, inlineNode, el, tagName) {
+ /*jshint unused: false*/
+ };
+
// Parse annotated text
// --------------------
// Make sure you call this method only for nodes where `this.isParagraphish(node) === true`
@@ -2194,6 +2415,10 @@ NlmToLensConverter.Prototype = function() {
}
}
}
+ else if (this.isInlineNode(type)) {
+ plainText += " ";
+ this.createInlineNode(state, el, charPos);
+ }
// Unsupported...
else if (!breakOnUnknown) {
if (state.top().ignore.indexOf(type) < 0) {
@@ -2203,7 +2428,7 @@ NlmToLensConverter.Prototype = function() {
}
} else {
if (nested) {
- console.error("Node not yet supported in annoted text: " + type);
+ console.error("Node not supported in annoted text: " + type +"\n"+el.outerHTML);
}
else {
// on paragraph level other elements can break a text block
@@ -2397,7 +2622,7 @@ NlmToLensConverter.State = function(converter, xmlDoc, doc) {
// of processed nodes to be able to associate other things (e.g., annotations) correctly.
this.stack = [];
- this.sectionLevel = 1;
+ this.sectionLevel = 0;
// Tracks all available affiliations
this.affiliations = [];
diff --git a/extensions/math/index.js b/extensions/math/index.js
index f2a431a3..7e76711c 100644
--- a/extensions/math/index.js
+++ b/extensions/math/index.js
@@ -3,6 +3,5 @@ module.exports = {
MathPanel: require("./math_panel"),
MathNodes: require('./nodes'),
ToggleFormula: require("./workflows/toggle_formula"),
- ToggleMathEnvironment: require("./workflows/toggle_math_environment"),
- ZoomFormula: require("./workflows/zoom_formula")
+ ToggleMathEnvironment: require("./workflows/toggle_math_environment")
};
\ No newline at end of file
diff --git a/extensions/math/math_converter.js b/extensions/math/math_converter.js
index 76fcc428..0bf8973b 100644
--- a/extensions/math/math_converter.js
+++ b/extensions/math/math_converter.js
@@ -1,8 +1,8 @@
var _ = require('underscore');
-var util = require("lens/substance/util");
-var LensConverter = require('lens/converter');
-var LensArticle = require("lens/article");
+var util = require("../../substance/util");
+var LensConverter = require('../../converter');
+var LensArticle = require("../../article");
var MathNodeTypes = require("./nodes");
// Options:
@@ -21,6 +21,14 @@ MathConverter.Prototype = function MathConverterPrototype() {
this._refTypeMapping["disp-formula"] = "formula_reference";
this._refTypeMapping["statement"] = "math_environment_reference";
+ this.acceptedParagraphElements = _.extend(__super__.acceptedParagraphElements, {
+ "def-list": { handler: 'defList' }
+ });
+
+ this._annotationTypes = _.extend(__super__._annotationTypes, {
+ "roman": "custom_annotation"
+ });
+
this.test = function(xmlDoc, documentUrl) {
/* jshint unused:false */
var publisherName = xmlDoc.querySelector("publisher-name").textContent;
@@ -67,6 +75,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
return doc;
};
+
// TODO: the default implemenation should be plain, i.e. not adding an extra heading 'Main Text'
// Instead the LensConverter should override this...
// ...or we should consider adding an option (if the eLife way to do it is more often applicable...)
@@ -96,7 +105,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
}
};
- this._bodyNodes['def-list'] = function(state, defList) {
+ this._bodyNodes['def-list'] = this.defList = function(state, defList) {
var enumerationNode = {
type: 'enumeration',
id: state.nextId('enumeration'),
@@ -105,14 +114,36 @@ MathConverter.Prototype = function MathConverterPrototype() {
var defItems = this.selectDirectChildren(defList, 'def-item');
for (var i = 0; i < defItems.length; i++) {
var defItem = defItems[i];
+ var term = defItem.querySelector('term');
+ var termId = term.id;
+ var def = defItem.querySelector('def');
var enumItemNode = {
type: 'enumeration-item',
+ // TODO: enabling the correct id makes warnings disappear
+ // which are given when seeing references to this def
+ // However, to work properly, we would need nesting support
+ // for definition references
+ // so we leave it for now
+ // id: termId || state.nextId('enumeration-item'),
id: state.nextId('enumeration-item'),
+ children: []
};
- var term = defItem.querySelector('term');
+ // convert label
enumItemNode.label = this.annotatedText(state, term, [enumItemNode.id, 'label']);
- var content = defItem.querySelector('def p');
- enumItemNode.children = _.pluck(this.paragraphGroup(state, content), "id");
+ // convert content
+ // TODO: is the assumption correct that def-item content is always wrapped in a p element?
+ var pEls = this.selectDirectChildren(def, 'p');
+ for (var j = 0; j < pEls.length; j++) {
+ var p = pEls[j];
+ var children = this.paragraphGroup(state, p);
+ var pgroup = {
+ type: 'paragraph',
+ id: state.nextId('pgroup'),
+ children: _.pluck(children, 'id')
+ };
+ state.doc.create(pgroup);
+ enumItemNode.children.push(pgroup.id);
+ }
state.doc.create(enumItemNode);
enumerationNode.items.push(enumItemNode.id);
}
@@ -120,6 +151,10 @@ MathConverter.Prototype = function MathConverterPrototype() {
return enumerationNode;
};
+ // HACK: There is content that has nested elements, which is not allowed
+ // we just treat them as sections
+ this._bodyNodes['app'] = this._bodyNodes['sec'];
+
this.extractDefinitions = function(/*state, article*/) {
// We don't want to show a definitions (glossary) panel
// TODO: we should consider making this a static configuration for lens-converter
@@ -276,7 +311,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
this._getFormulaData = function(state, formulaElement, formulaId, inline) {
var result = [];
- var labels = {'tex' : {}, 'svg': {}, 'math': {}};
+ var labels = {'tex' : {}, 'svg': {}, 'html': {}, 'math': {}};
var el = formulaElement;
var alternatives = el.querySelector('alternatives');
if (alternatives) el = alternatives;
@@ -297,6 +332,13 @@ MathConverter.Prototype = function MathConverterPrototype() {
data: this.toHtml(child)
});
break;
+ case "textual-form":
+ labels.html = this._extractLabels(child);
+ result.push({
+ format: "html",
+ data: $(child).text()
+ });
+ break;
case "mml:math":
case "math":
// HACK: make sure that mml in display-formulas has set display="block"
@@ -342,6 +384,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
state.labelsForFormula[formulaId] = labels;
return result;
};
+
this.formula = function(state, formulaElement, inline) {
var doc = state.doc;
var id = state.nextId("formula");
@@ -496,6 +539,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
if (type === 'label' || !el.textContent) continue;
institutionText += el.textContent;
}
+ var specific_use = aff.getAttribute('specific-use');
// TODO: we might add a property to the affiliation node that collects
// data which is not handled here
@@ -505,7 +549,8 @@ MathConverter.Prototype = function MathConverterPrototype() {
type: "affiliation",
source_id: aff.getAttribute("id"),
label: label ? label.textContent : null,
- institution: institutionText
+ institution: institutionText,
+ specific_use: specific_use || null
};
state.affiliations.push(affiliationNode.id);
@@ -539,7 +584,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
// lipid droplet
// anti-bacterial
//
- var keyWords = articleMeta.querySelectorAll("kwd-group kwd");
+ var keywordEls = articleMeta.querySelectorAll("kwd-group kwd");
// Extract subjects
// ------------
@@ -551,7 +596,7 @@ MathConverter.Prototype = function MathConverterPrototype() {
// Microbiology and infectious disease
//
- var subjects = articleMeta.querySelectorAll("subj-group[subj-group-type=heading] subject");
+ var subjectEls = articleMeta.querySelectorAll("subj-group[subj-group-type=heading] subject");
// Article Type
//
@@ -596,8 +641,18 @@ MathConverter.Prototype = function MathConverterPrototype() {
publicationInfo.raw_formats = rawFormats;
- publicationInfo.keywords = _.pluck(keyWords, "textContent");
- publicationInfo.subjects = _.pluck(subjects, "textContent");
+ var keywords = [];
+ for (var i = 0; i < keywordEls.length; i++) {
+ keywords.push(this.annotatedText(state, keywordEls[i], ["publication_info", "keywords", i]));
+ }
+ publicationInfo.keywords = keywords;
+
+ var subjects = [];
+ for (var i = 0; i < subjectEls.length; i++) {
+ subjects.push(this.annotatedText(state, subjectEls[i], ["publication_info", "subjects", i]));
+ }
+ publicationInfo.subjects = subjects;
+
publicationInfo.article_type = articleType ? articleType.textContent : "";
publicationInfo.links = links;
};
@@ -672,14 +727,17 @@ MathConverter.Prototype = function MathConverterPrototype() {
anno.target = targetNode.id;
} else {
console.log("Could not lookup math environment for reference", anno);
+ continue;
}
referencedMath[targetNode.id] = true;
} else {
targetNode = state.doc.getNodeBySourceId(anno.target) || state.doc.get(anno.target);
if (targetNode) {
anno.target = targetNode.id;
+ targetNode.isReferenced = true;
} else {
console.log("Could not lookup targetNode for annotation", anno);
+ continue;
}
}
}
@@ -696,58 +754,91 @@ MathConverter.Prototype = function MathConverterPrototype() {
state.referencedMath = referencedMath;
};
+ function _showFigure(state, node) {
+ // show all figures in the figures panel
+ state.doc.show('figures', node.id);
+ // show unreferenced and anchored figures in the main content
+ if (!node.isReferenced || node.position === 'anchor') {
+ state.doc.show('content', node.id);
+ }
+ }
+
+ function _showFormulaOrEnvironment(state, node, nested) {
+ var referencedMath = state.referencedMath;
+ var info = state.nodeInfo[node.id];
+ // only show formulas and environments in the math panel
+ // - if they are referenced
+ // - or have specificUse='resource' set explicitly
+ if (referencedMath[node.id] ||
+ (info && info.specificUse === "resource")) {
+ doc.show(MATH_PANEL, node.id);
+ }
+ if (!nested) {
+ doc.show('content', node.id);
+ }
+ // a math environment can have nested content
+ // such as figures or environments which need
+ // to be processed recursively
+ if (node.type === 'math_environment') {
+ _showNestedContent(state, node.body);
+ }
+ }
+
+ function _showNestedContent(state, nodeIds) {
+ var referencedMath = state.referencedMath;
+ for (var i = 0; i < nodeIds.length; i++) {
+ var nodeId = nodeIds[i]
+ var node = state.doc.get(nodeId);
+ var info = state.nodeInfo[nodeId];
+ switch (node.type) {
+ case 'figure':
+ // show all figures in the figures panel
+ state.doc.show('figures', nodeId);
+ // hide referenced and unanchored figures from the environment
+ if (node.isReferenced && node.position !== 'anchor') {
+ nodeIds.splice(i, 1);
+ i--;
+ }
+ break;
+ case 'formula':
+ case 'math_environment':
+ _showFormulaOrEnvironment(state, node, 'nested');
+ break;
+ default:
+ // nothing
+ }
+ }
+ }
+
+ function _showProof(state, node) {
+ // proofs are always shown only in the content
+ state.doc.show('content', node.id);
+ _showNestedContent(state, node.children);
+ }
+
this.populatePanels = function(state) {
var doc = state.doc;
var referencedMath = state.referencedMath;
var node, child, info;
for (var i = 0; i < state.shownNodes.length; i++) {
- node = state.shownNodes[i];
+ node = doc.get(state.shownNodes[i].id);
switch (node.type) {
case 'figure':
- // show figures without captions are only in-flow
- if (!node.caption) {
- state.doc.show('content', node.id);
- }
- // all others are shown in the figures panel
- else {
- state.doc.show('figures', node.id);
- // in addition a figure can be shown in-flow using position='anchor'
- if (node.position === 'anchor') {
- state.doc.show('content', node.id);
- }
- }
+ _showFigure(state, node);
break;
case 'formula':
case 'math_environment':
- info = state.nodeInfo[node.id];
- // only environments or formulas go into the math panel
- // that ar referenced or forced using `specific-use='resource'`
- if (referencedMath[node.id] ||
- (info && info.specificUse === "resource")) {
- doc.show(MATH_PANEL, node.id);
- }
- doc.show('content', node.id);
+ _showFormulaOrEnvironment(state, node);
break;
- // Special treatment for proofs as they may contain equations
- // which when referenced should be displayed in the resource panel
case 'proof':
- LensConverter.prototype.showNode.call(this, state, node);
- for (var j = 0; j < node.children.length; j++) {
- child = doc.get(node.children[j]);
- if (child.type === 'formula' || child.type === 'math_environment') {
- info = state.nodeInfo[child.id];
- if (referencedMath[child.id] ||
- (info && info.specificUse === "resource")) {
- doc.show(MATH_PANEL, child.id);
- }
- }
- }
+ _showProof(state, node);
break;
default:
LensConverter.prototype.showNode.call(this, state, node);
}
}
};
+
};
MathConverter.Prototype.prototype = LensConverter.prototype;
diff --git a/extensions/math/math_panel.js b/extensions/math/math_panel.js
index 9ec31814..72b8a240 100644
--- a/extensions/math/math_panel.js
+++ b/extensions/math/math_panel.js
@@ -1,6 +1,6 @@
"use strict";
-var Lens = require('lens/reader');
+var Lens = require('../../reader');
var ContainerPanel = Lens.ContainerPanel;
var ContainerPanelController = Lens.ContainerPanelController;
var ContainerPanelView = Lens.ContainerPanelView;
diff --git a/extensions/math/nodes/citation/citation_view.js b/extensions/math/nodes/citation/citation_view.js
index 5d376fa1..f9b88e5a 100644
--- a/extensions/math/nodes/citation/citation_view.js
+++ b/extensions/math/nodes/citation/citation_view.js
@@ -1,6 +1,6 @@
"use strict";
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../article/nodes');
var LensCitationView = LensNodes['citation'].View;
diff --git a/extensions/math/nodes/citation/index.js b/extensions/math/nodes/citation/index.js
index 8fca51c3..e9672eca 100644
--- a/extensions/math/nodes/citation/index.js
+++ b/extensions/math/nodes/citation/index.js
@@ -1,4 +1,4 @@
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
module.exports = {
Model: LensNodes['citation'].Model,
diff --git a/extensions/math/nodes/enumeration/enumeration.js b/extensions/math/nodes/enumeration/enumeration.js
index caa5dbd3..ca56d1c0 100644
--- a/extensions/math/nodes/enumeration/enumeration.js
+++ b/extensions/math/nodes/enumeration/enumeration.js
@@ -1,7 +1,7 @@
"use strict";
var _ = require("underscore");
-var Document = require("lens/substance/document");
+var Document = require("../../../../substance/document");
var DocumentNode = Document.Node;
var Composite = Document.Composite;
diff --git a/extensions/math/nodes/enumeration/enumeration_view.js b/extensions/math/nodes/enumeration/enumeration_view.js
index 57d680fe..0403be47 100644
--- a/extensions/math/nodes/enumeration/enumeration_view.js
+++ b/extensions/math/nodes/enumeration/enumeration_view.js
@@ -1,6 +1,6 @@
"use strict";
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
var NodeView = LensNodes['node'].View;
var CompositeView = LensNodes['composite'].View;
diff --git a/extensions/math/nodes/enumeration_item/enumeration_item.js b/extensions/math/nodes/enumeration_item/enumeration_item.js
index 092c7c72..03a1e69d 100644
--- a/extensions/math/nodes/enumeration_item/enumeration_item.js
+++ b/extensions/math/nodes/enumeration_item/enumeration_item.js
@@ -1,7 +1,7 @@
"use strict";
var _ = require('underscore');
-var Document = require('lens/substance/document');
+var Document = require('../../../../substance/document');
var EnumerationItem = function(node, doc) {
Document.Composite.call(this, node, doc);
diff --git a/extensions/math/nodes/enumeration_item/enumeration_item_view.js b/extensions/math/nodes/enumeration_item/enumeration_item_view.js
index f9d84f92..d2de789d 100644
--- a/extensions/math/nodes/enumeration_item/enumeration_item_view.js
+++ b/extensions/math/nodes/enumeration_item/enumeration_item_view.js
@@ -1,7 +1,7 @@
"use strict";
-var LensNodes = require("lens/article/nodes");
-var $$ = require("lens/substance/application").$$;
+var LensNodes = require("../../../../article/nodes");
+var $$ = require("../../../../substance/application").$$;
var NodeView = LensNodes["node"].View;
var CompositeView = LensNodes["composite"].View;
diff --git a/extensions/math/nodes/formula/formula.js b/extensions/math/nodes/formula/formula.js
index 7c723803..8e20c2d3 100644
--- a/extensions/math/nodes/formula/formula.js
+++ b/extensions/math/nodes/formula/formula.js
@@ -1,6 +1,6 @@
"use strict";
-var Document = require('lens/substance/document');
+var Document = require('../../../../substance/document');
// Formula
// -----------------
diff --git a/extensions/math/nodes/formula/formula_view.js b/extensions/math/nodes/formula/formula_view.js
index ef2ac480..ffb2ec0d 100644
--- a/extensions/math/nodes/formula/formula_view.js
+++ b/extensions/math/nodes/formula/formula_view.js
@@ -1,12 +1,12 @@
"use strict";
var _ = require('underscore');
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
var NodeView = LensNodes["node"].View;
-var LensArticle = require('lens/article');
+var LensArticle = require('../../../../article');
var ResourceView = LensArticle.ResourceView;
-var $$ = require('lens/substance/application').$$;
+var $$ = require('../../../../substance/application').$$;
// FormulaView
// ===========
@@ -153,6 +153,14 @@ FormulaView.Prototype = function() {
hasPreview = true;
}
break;
+ case "html":
+ // add only if no preview
+ if (!hasPreview) {
+ // don't use a preview element
+ this.$content.append($(data));
+ hasPreview = true;
+ }
+ break;
default:
console.error("Unknown formula format:", format);
}
diff --git a/extensions/math/nodes/formula_reference/formula_reference.js b/extensions/math/nodes/formula_reference/formula_reference.js
index 859762c4..aab49483 100644
--- a/extensions/math/nodes/formula_reference/formula_reference.js
+++ b/extensions/math/nodes/formula_reference/formula_reference.js
@@ -1,7 +1,7 @@
"use strict";
-var Document = require('lens/substance/document');
-var LensNodes = require('lens/article/nodes');
+var Document = require('../../../../substance/document');
+var LensNodes = require('../../../../article/nodes');
var Annotation = LensNodes['annotation'].Model;
var ResourceReference = LensNodes['resource_reference'].Model;
diff --git a/extensions/math/nodes/formula_reference/index.js b/extensions/math/nodes/formula_reference/index.js
index 07a78d82..f6a72b7e 100644
--- a/extensions/math/nodes/formula_reference/index.js
+++ b/extensions/math/nodes/formula_reference/index.js
@@ -1,4 +1,4 @@
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
module.exports = {
Model: require('./formula_reference.js'),
diff --git a/extensions/math/nodes/math_environment/math_environment.js b/extensions/math/nodes/math_environment/math_environment.js
index dfed8829..93a6a704 100644
--- a/extensions/math/nodes/math_environment/math_environment.js
+++ b/extensions/math/nodes/math_environment/math_environment.js
@@ -1,6 +1,6 @@
"use strict";
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
var DocumentNode = LensNodes['node'].Model;
var MathEnvironment = function(node, document) {
@@ -8,6 +8,7 @@ var MathEnvironment = function(node, document) {
};
MathEnvironment.type = {
+ "id": "math_environment",
"parent": "content",
"properties": {
"source_id": "string",
diff --git a/extensions/math/nodes/math_environment/math_environment_view.js b/extensions/math/nodes/math_environment/math_environment_view.js
index 9b93e380..31257507 100644
--- a/extensions/math/nodes/math_environment/math_environment_view.js
+++ b/extensions/math/nodes/math_environment/math_environment_view.js
@@ -1,12 +1,12 @@
"use strict";
var _ = require('underscore');
-var LensArticle = require('lens/article');
-var LensNodes = require('lens/article/nodes');
+var LensArticle = require('../../../../article');
+var LensNodes = require('../../../../article/nodes');
var NodeView = LensNodes["node"].View;
var ResourceView = LensArticle.ResourceView;
-var $$ = require('lens/substance/application').$$;
+var $$ = require('../../../../substance/application').$$;
// Lens.MathEnvironment.View
// ==========================================================================
diff --git a/extensions/math/nodes/math_environment_reference/index.js b/extensions/math/nodes/math_environment_reference/index.js
index 7299bae3..e693d54c 100644
--- a/extensions/math/nodes/math_environment_reference/index.js
+++ b/extensions/math/nodes/math_environment_reference/index.js
@@ -1,4 +1,4 @@
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
module.exports = {
Model: require('./math_environment_reference.js'),
diff --git a/extensions/math/nodes/math_environment_reference/math_environment_reference.js b/extensions/math/nodes/math_environment_reference/math_environment_reference.js
index cadfaf09..c1224b10 100644
--- a/extensions/math/nodes/math_environment_reference/math_environment_reference.js
+++ b/extensions/math/nodes/math_environment_reference/math_environment_reference.js
@@ -1,7 +1,7 @@
"use strict";
-var Document = require('lens/substance/document');
-var LensNodes = require('lens/article/nodes');
+var Document = require('../../../../substance/document');
+var LensNodes = require('../../../../article/nodes');
var Annotation = LensNodes['annotation'].Model;
var ResourceReference = LensNodes['resource_reference'].Model;
diff --git a/extensions/math/nodes/plain_citation/plain_citation.js b/extensions/math/nodes/plain_citation/plain_citation.js
index d6544f64..affad7ec 100644
--- a/extensions/math/nodes/plain_citation/plain_citation.js
+++ b/extensions/math/nodes/plain_citation/plain_citation.js
@@ -1,6 +1,6 @@
"use strict";
-var Document = require('lens/substance/document');
+var Document = require('../../../../substance/document');
var PlainCitation = function(node, doc) {
Document.Node.call(this, node, doc);
diff --git a/extensions/math/nodes/plain_citation/plain_citation_view.js b/extensions/math/nodes/plain_citation/plain_citation_view.js
index faed8b2d..bd638eb9 100644
--- a/extensions/math/nodes/plain_citation/plain_citation_view.js
+++ b/extensions/math/nodes/plain_citation/plain_citation_view.js
@@ -6,11 +6,11 @@ var LABELS = {
};
var _ = require('underscore');
-var LensArticle = require('lens/article');
-var LensNodes = require('lens/article/nodes');
+var LensArticle = require('../../../../article');
+var LensNodes = require('../../../../article/nodes');
var NodeView = LensNodes['node'].View;
var ResourceView = LensArticle.ResourceView;
-var $$ = require("lens/substance/application").$$;
+var $$ = require("../../../../substance/application").$$;
// Lens.Citation.View
// ==========================================================================
diff --git a/extensions/math/nodes/proof/proof.js b/extensions/math/nodes/proof/proof.js
index b91e0e7d..0adc25bd 100644
--- a/extensions/math/nodes/proof/proof.js
+++ b/extensions/math/nodes/proof/proof.js
@@ -1,6 +1,6 @@
"use strict";
-var Document = require('lens/substance/document');
+var Document = require('../../../../substance/document');
var Composite = Document.Composite;
// Lens.Proof
diff --git a/extensions/math/nodes/proof/proof_view.js b/extensions/math/nodes/proof/proof_view.js
index bf415745..8186afcd 100644
--- a/extensions/math/nodes/proof/proof_view.js
+++ b/extensions/math/nodes/proof/proof_view.js
@@ -1,9 +1,9 @@
"use strict";
-var LensNodes = require('lens/article/nodes');
+var LensNodes = require('../../../../article/nodes');
var NodeView = LensNodes["node"].View;
var CompositeView = LensNodes["composite"].View;
-var $$ = require("lens/substance/application").$$;
+var $$ = require("../../../../substance/application").$$;
// Lens.Proof.View
diff --git a/extensions/math/workflows/toggle_formula.js b/extensions/math/workflows/toggle_formula.js
index cd813065..3508f031 100644
--- a/extensions/math/workflows/toggle_formula.js
+++ b/extensions/math/workflows/toggle_formula.js
@@ -1,5 +1,5 @@
var _ = require('underscore');
-var Lens = require('lens/reader');
+var Lens = require('../../../reader');
var Workflow = Lens.Workflow;
var ToggleFormula = function() {
diff --git a/extensions/math/workflows/toggle_math_environment.js b/extensions/math/workflows/toggle_math_environment.js
index 84d5afc0..57896e36 100644
--- a/extensions/math/workflows/toggle_math_environment.js
+++ b/extensions/math/workflows/toggle_math_environment.js
@@ -1,5 +1,5 @@
var _ = require('underscore');
-var Lens = require('lens/reader');
+var Lens = require('../../../reader');
var Workflow = Lens.Workflow;
var ToggleMathEnvironment = function() {
diff --git a/extensions/math/workflows/zoom_formula.js b/extensions/math/workflows/zoom_formula.js
deleted file mode 100644
index a1e6e46f..00000000
--- a/extensions/math/workflows/zoom_formula.js
+++ /dev/null
@@ -1,159 +0,0 @@
-var _ = require('underscore');
-var Lens = require('lens/reader');
-var Workflow = Lens.Workflow;
-
-var ZoomFormula = function() {
- Workflow.call(this);
-
- this.formulaWidths = {};
- this.formulaHeights = {};
- this.formulaIsZoomed = {};
-
- this._handleFormulasChange = function() {
- this.fitFormulas();
- }.bind(this);
-
-};
-
-ZoomFormula.Prototype = function() {
-
- this.registerHandlers = function() {
- var self = this;
-
- // Listen for clicks on formulas to toggle scale/scroll
- // ------------------
- //
- // this way to handle the event is a hack! lens/substance infrastructure should be used here!
-
- $(this.readerView.$el).on("click",".formula .content .MathJax_Display.zoomable",
- function(e) { self.toggleFormulaScaling(e,this); }
- );
-
- // Attach a lazy/debounced handler for resize events
- // ------------------
- //
-
- $(window).resize(_.debounce(_.bind(function() {
- this.fitFormulas();
- }, this), 1));
-
- // Recompute zoom factors after MathJax has finished processing.
- // ------------------
- //
-
- var self = this;
- MathJax.Hub.Register.MessageHook("End Process", function (message) {
- self.fitFormulas();
- });
-
- this.readerView.doc.on('app:formulas:changed', this._handleFormulasChange);
- };
-
- this.unRegisterHandlers = function() {
- $(this.readerView.$el).off("click",".formula .content .MathJax_Display");
- this.readerView.doc.off('app:formulas:changed', this._handleFormulasChange);
- };
-
- this.fitFormula = function(nodeId,formulaNode) {
- var mathjaxContainer = $(formulaNode).find('.MathJax_Display')[0];
- var mathEl = $(formulaNode).find('.math')[0];
- if (!mathEl) return;
-
- if(this.getFormulaIsZoomed(nodeId)) { // Zoomed
-
- mathEl.style["-webkit-transform-origin"] = "";
- mathEl.style["-moz-transform-origin"] = "";
- mathEl.style["-ms-transform-origin"] = "";
- mathEl.style.transformOrigin = "";
- mathEl.style["-webkit-transform"] = "";
- mathEl.style["-moz-transform"] = "";
- mathEl.style["-ms-transform"] = "";
- mathEl.style.transform = "";
-
- mathjaxContainer.style.height = "";
- mathjaxContainer.style.overflowX = "auto";
-
- } else { // Scaled
- var containerWidth = $(mathjaxContainer).width();
- var INDENT = 3.0;
- var style;
-
- if (!this.formulaWidths[nodeId]) {
- var spanElement = $(mathjaxContainer).find(".math")[0];
- if (spanElement) {
- style = window.getComputedStyle(spanElement);
- this.formulaWidths[nodeId] = parseFloat(style.fontSize) * (spanElement.bbox.w + INDENT);
- }
- }
-
- if (!this.formulaHeights[nodeId]) {
- style = window.getComputedStyle(mathjaxContainer);
- this.formulaHeights[nodeId] = parseFloat(style.height);
- }
- var CORRECTION_FACTOR = 0.92;
- var ratio = Math.min(containerWidth / this.formulaWidths[nodeId]*CORRECTION_FACTOR,1.0);
- // mathEl.style.cursor = (ratio < 0.9 ? "zoom-in" : ""); //simple zoom UI
- if (ratio < 0.9) {
- mathjaxContainer.classList.add("zoomable");
- } else {
- mathjaxContainer.classList.remove("zoomable");
- }
-
- mathEl.style["-webkit-transform-origin"] = "top left";
- mathEl.style["-moz-transform-origin"] = "top left";
- mathEl.style["-ms-transform-origin"] = "top left";
- mathEl.style.transformOrigin = "top left";
- mathEl.style["-webkit-transform"] = "scale("+ratio+")";
- mathEl.style["-moz-transform"] = "scale("+ratio+")";
- mathEl.style["-ms-transform"] = "scale("+ratio+")";
- mathEl.style.transform = "scale("+ratio+")";
-
- mathjaxContainer.style.height = "" + (this.formulaHeights[nodeId] * ratio) + "px";
- mathjaxContainer.style.overflowX = "visible";
- }
- };
-
- // Trigger fitting all formulas in the document
- // -----------------
-
- this.fitFormulas = function() {
- var self = this;
-
- $('.content-node.formula').each(function() {
- var nodeId = $(this).find('.MathJax_Display .MathJax').attr("id");
- self.fitFormula(nodeId,this);
- });
- };
-
- // these methods handle the toggling logic
- // -----------------
-
- this.getFormulaIsZoomed = function(nodeId) {
- if(this.formulaIsZoomed[nodeId] === undefined) {
- return false;
- } else {
- return this.formulaIsZoomed[nodeId];
- }
- };
-
- this.setFormulaIsZoomed = function(nodeId,value) {
- this.formulaIsZoomed[nodeId] = value;
- };
-
- this.toggleFormulaIsZoomed = function(nodeId) {
- this.setFormulaIsZoomed(nodeId, !this.getFormulaIsZoomed(nodeId));
- };
-
- this.toggleFormulaScaling = function(e, node) {
- // var nodeId = $(e.currentTarget).find('.MathJax').attr("id");
- var nodeId = $(node).find('.MathJax').attr("id");
- var formulaNode = $(node).parents('.content-node.formula')[0];
- this.toggleFormulaIsZoomed(nodeId);
- this.fitFormula(nodeId, formulaNode);
- };
-
-};
-ZoomFormula.Prototype.prototype = Workflow.prototype;
-ZoomFormula.prototype = new ZoomFormula.Prototype();
-
-module.exports = ZoomFormula;
diff --git a/maintainers.txt b/maintainers.txt
new file mode 100644
index 00000000..39a7775b
--- /dev/null
+++ b/maintainers.txt
@@ -0,0 +1 @@
+gnott
diff --git a/package.json b/package.json
index 0ed13374..cd59ada3 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "lens",
"version": "2.1.0",
"description": "A novel way of seeing content.",
- "url": "http://github.com/elifesciences/lens",
+ "url": "https://github.com/elifesciences/lens",
"keywords": [
"digital documents",
"linked-data",
@@ -26,7 +26,7 @@
"url": "https://github.com/elifesciences/lens.git"
},
"dependencies": {
- "underscore": "1.8.3"
+ "underscore": "1.12.1"
},
"engines": {
diff --git a/reader/lens.js b/reader/lens.js
index 7d8a74bf..a15cd22f 100644
--- a/reader/lens.js
+++ b/reader/lens.js
@@ -1,9 +1,9 @@
"use strict";
-var Application = require("../substance/application");
var LensController = require("./lens_controller");
-var LensConverter = require("lens/converter");
-var LensArticle = require("lens/article");
+var LensConverter = require("../converter");
+var Application = require("../substance/application");
+var LensArticle = require("../article");
var ResourcePanelViewFactory = require("./panels/resource_panel_viewfactory");
var ReaderController = require('./reader_controller');
var ReaderView = require('./reader_view');
diff --git a/reader/lens_controller.js b/reader/lens_controller.js
index f385e34c..7f5222ec 100644
--- a/reader/lens_controller.js
+++ b/reader/lens_controller.js
@@ -5,8 +5,8 @@ var util = require("../substance/util");
var Controller = require('../substance/application').Controller;
var LensView = require("./lens_view");
var ReaderController = require('./reader_controller');
-var LensArticle = require('lens/article');
-var NLMConverter = require('lens/converter');
+var LensArticle = require('../article');
+var NLMConverter = require('../converter');
// Lens.Controller
diff --git a/reader/panels/container_panel_view.js b/reader/panels/container_panel_view.js
index 43baa6e8..b98d1467 100644
--- a/reader/panels/container_panel_view.js
+++ b/reader/panels/container_panel_view.js
@@ -4,8 +4,7 @@ var _ = require("underscore");
var Scrollbar = require("./surface_scrollbar");
var Surface = require("../lens_surface");
var PanelView = require("./panel_view");
-
-var MENU_BAR_HEIGHT = 40;
+var getRelativeBoundingRect = require('../../substance/util/getRelativeBoundingRect');
// TODO: try to get rid of DocumentController and use the Container node instead
var ContainerPanelView = function( panelCtrl, viewFactory, config ) {
@@ -34,12 +33,12 @@ ContainerPanelView.Prototype = function() {
this.render = function() {
// Hide the whole tab if there is no content
- if (this.getContainer().getLength() === 0) {
- this.hideToggle();
- this.hide();
- } else {
+ if (this.getContainer().hasContent(this.config.type)) {
this.surface.render();
this.scrollbar.render();
+ } else {
+ this.hideToggle();
+ this.hide();
}
return this;
};
@@ -59,29 +58,21 @@ ContainerPanelView.Prototype = function() {
this.scrollTo = function(nodeId) {
var n = this.findNodeView(nodeId);
if (n) {
- var $n = $(n);
-
- var windowHeight = $(window).height();
var panelHeight = this.surface.$el.height();
- var scrollTop;
+ var screenTop = this.surface.$el.scrollTop();
+ var screenBottom = screenTop + panelHeight;
+ var elRect = getRelativeBoundingRect([n], this.surface.$nodes[0]);
+ var elHeight = elRect.height;
+
+ var upperBound = elRect.top; // top-offset of upper bound to relative parent
+ var lowerBound = upperBound+elRect.height; // top-offset of lower bound to relative parent
- scrollTop = this.surface.$el.scrollTop();
- var elTop = $n.offset().top;
- var elHeight = $n.height();
- var topOffset;
// Do not scroll if the element is fully visible
- if ((elTop > 0 && elTop + elHeight < panelHeight) || (elTop >= 0 && elTop < panelHeight)) {
- // everything fine
+ if (upperBound>=screenTop && lowerBound <= screenBottom) {
return;
}
- // In all other cases scroll to the top of the element
- else {
- // HACK: we subtract the height of the menu bar to the scroll position,
- // because elTop does not consider the offset
- topOffset = scrollTop + elTop - MENU_BAR_HEIGHT;
- }
- this.surface.$el.scrollTop(topOffset);
+ this.surface.$el.scrollTop(upperBound);
this.scrollbar.update();
} else {
console.info("ContainerPanelView.scrollTo(): Unknown resource '%s'", nodeId);
diff --git a/reader/panels/content/content_panel_view.js b/reader/panels/content/content_panel_view.js
index 65a405ae..7877486e 100644
--- a/reader/panels/content/content_panel_view.js
+++ b/reader/panels/content/content_panel_view.js
@@ -59,8 +59,7 @@ ContentPanelView.Prototype = function() {
this.onTocItemSelected = function(nodeId) {
var n = this.findNodeView(nodeId);
if (n) {
- var topOffset = $(n).position().top+CORRECTION;
- this.surface.$el.scrollTop(topOffset);
+ n.scrollIntoView();
}
};
@@ -136,4 +135,4 @@ ContentPanelView.Prototype.prototype = ContainerPanelView.prototype;
ContentPanelView.prototype = new ContentPanelView.Prototype();
ContentPanelView.prototype.constructor = ContentPanelView;
-module.exports = ContentPanelView;
\ No newline at end of file
+module.exports = ContentPanelView;
diff --git a/reader/panels/panel_view.js b/reader/panels/panel_view.js
index 32edfab0..3bf5aade 100644
--- a/reader/panels/panel_view.js
+++ b/reader/panels/panel_view.js
@@ -127,7 +127,6 @@ PanelView.Prototype = function() {
return this.el.querySelector('*[data-id='+nodeId+']');
};
-
// Event handling
// --------
//
diff --git a/reader/reader_view.js b/reader/reader_view.js
index 986924ce..40b18954 100644
--- a/reader/reader_view.js
+++ b/reader/reader_view.js
@@ -31,8 +31,6 @@ var ReaderView = function(readerCtrl) {
// Note: ATM, it is not possible to override the content panel + toc via panelSpecification
this.contentView = readerCtrl.panelCtrls.content.createView();
this.tocView = this.contentView.getTocView();
-
-
this.panelViews = {};
// mapping to associate reference types to panels
// NB, in Lens each resource type has one dedicated panel;
@@ -78,6 +76,7 @@ var ReaderView = function(readerCtrl) {
this.listenTo(panelView, "toggle-resource-reference", this.onToggleResourceReference);
this.listenTo(panelView, "toggle-fullscreen", this.onToggleFullscreen);
}, this);
+
// TODO: treat content panel as panelView and delegate to tocView where necessary
this.listenTo(this.contentView, "toggle", this._onTogglePanel);
this.listenTo(this.contentView, "toggle-resource", this.onToggleResource);
@@ -165,11 +164,13 @@ ReaderView.Prototype = function() {
var self = this;
// MathJax requires the processed elements to be in the DOM
- window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub]);
- window.MathJax.Hub.Queue(function () {
- // HACK: using updateState() instead of updateScrollbars() as it also knows how to scroll
- self.updateState();
- });
+ if (window.MathJax){
+ window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub]);
+ window.MathJax.Hub.Queue(function () {
+ // HACK: using updateState() instead of updateScrollbars() as it also knows how to scroll
+ self.updateState();
+ });
+ }
}, this), 1);
return this;
@@ -248,6 +249,7 @@ ReaderView.Prototype = function() {
// HACK: abusing addHighlight for adding the fullscreen class
// instead I would prefer to handle such focussing explicitely in a workflow
if (state.fullscreen) classes.push("fullscreen");
+ this.contentView.addHighlight(state.focussedNode, classes.concat('main-occurrence').join(' '));
currentPanelView.addHighlight(state.focussedNode, classes.join(' '));
currentPanelView.scrollTo(state.focussedNode);
}
@@ -314,7 +316,7 @@ ReaderView.Prototype = function() {
self.updateScrollbars();
_.delay(function() {
- self.updateScrollbars();
+ self.updateScrollbars();
}, 2000);
};
diff --git a/styles/default.scss b/styles/default.scss
new file mode 100644
index 00000000..90472dc1
--- /dev/null
+++ b/styles/default.scss
@@ -0,0 +1,306 @@
+/* Default style
+=============================== */
+
+$interface-font: "Avenir Next", "Helvetica", arial, sans-serif;
+$prose-font: "PT Serif", "Georgia", serif;
+$default-text-color: #212121;
+
+/* Used as a mixin */
+.contextual-label {
+ font-family: $interface-font;
+ font-weight: 600;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: .5px;
+}
+
+html {
+ -webkit-font-smoothing: inherit;
+}
+body {
+ font-family: $interface-font;
+ font-weight: 500;
+ color: $default-text-color;
+}
+
+/* Links */
+a {
+ color: #0277BD;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #0277BD;
+}
+
+/* Line height overrides */
+.article .resources {
+ line-height: 24px;
+}
+
+/* Bread crumbs */
+.lens-article .content-node.cover .breadcrumbs a {
+ @extend .contextual-label;
+}
+
+/* Article title */
+.lens-article .content-node.cover .title {
+ font-size: 36px;
+ line-height: 48px;
+ font-weight: 600;
+}
+
+/* Published date */
+.lens-article .content-node.cover .published-on {
+ @extend .contextual-label;
+}
+
+.lens-article .content-node.cover .subjects {
+ @extend .contextual-label;
+}
+
+.lens-article .content-node.cover .subjects a {
+ font-weight: 600;
+}
+
+/* Authors on cover page */
+.lens-article .content-node.cover .doi {
+ @extend .contextual-label;
+ border-top: 1px solid #e0e0e0;
+ border-bottom: 1px solid #e0e0e0;
+ margin: 24px 0;
+ padding: 11px 0;
+
+ a {
+ text-transform: none;
+ color: #212121;
+ font-weight: 600;
+ }
+}
+
+.lens-article .content-node.cover .authors .text a {
+ font-weight: 600;
+ font-size: 16px;
+ line-height: 24px;
+ margin: 0 0 12px;
+ color: $default-text-color;
+}
+
+.lens-article .content-node.cover .authors .text a:hover {
+ background: none;
+ // border-bottom: 1px solid #212121;
+ color: #0277BD;
+}
+
+.lens-article .content-node.cover .authors .contributor_reference.highlighted {
+ border-bottom: 1px solid #0277BD;
+ background: none;
+ color: #0277BD;
+}
+
+/* Links on cover page (PDF, Source XML, Lens JSON) */
+.lens-article .content-node.cover .content .links {
+ display: none; // hide links to PDF, Source XML, Lens JSON
+}
+.lens-article .content-node.cover .content .links a {
+ font-weight: 600;
+ @extend .contextual-label;
+}
+
+/* Paragraph (only in main content) */
+.surface.content .content-node.paragraph {
+ font-family: $prose-font;
+ line-height: 1.5;
+ font-size: 16px;
+}
+
+/* Headings */
+.content-node.heading.level-1 .content {
+ font-size: 26px;
+ line-height: 24px;
+ padding-top: 24px;
+ font-weight: 600;
+}
+
+.content-node.heading.level-2 .content {
+ font-size: 22px;
+ line-height: 24px;
+ padding-top: 12px;
+ font-weight: 600;
+}
+.surface.content .nodes > .content-node.heading.level-2 {
+ padding-bottom: 0px;
+}
+
+.content-node.heading.level-3 .content {
+ font-size: 20px;
+ line-height: 24px;
+ padding-top: 12px;
+ font-weight: 600;
+}
+.surface.content .nodes > .content-node.heading.level-3 {
+ padding-bottom: 0px;
+}
+
+.content-node.heading.level-4 .content {
+ font-size: 18px;
+ line-height: 24px;
+ padding-top: 12px;
+ font-weight: 600;
+}
+.surface.content .nodes > .content-node.heading.level-4 {
+ padding-bottom: 0px;
+}
+
+.content-node.heading.level-5 .content {
+ font-size: 16px;
+ line-height: 24px;
+ padding-top: 12px;
+ font-weight: 600;
+}
+.surface.content .nodes > .content-node.heading.level-5 {
+ padding-bottom: 0px;
+}
+
+/* Headings in TOC */
+.resource-view.toc .heading-ref {
+ color: inherit; // reset color
+ padding: 0;
+ margin-bottom: 12px;
+ border: none;
+}
+
+.resource-view.toc .heading-ref.active {
+ color: inherit;
+ border: none;
+ span {
+ text-decoration: underline;
+ }
+}
+
+.resource-view.toc .heading-ref {
+ font-size: 16px;
+}
+
+.resource-view.toc .heading-ref.level-2 {
+ font-weight: 500;
+ font-size: 16px;
+ padding-left: 24px;
+}
+
+.resource-view.toc .heading-ref.level-3 {
+ font-weight: 500;
+ font-size: 16px;
+ padding-left: 48px;
+}
+
+.resource-view.toc .heading-ref.level-4 {
+ padding-left: 48px;
+}
+
+/* Reference card */
+.lens-article .resources .content-node.citation .resource-header .name {
+ font-weight: 600;
+}
+
+.lens-article .resources .content-node.contributor .resource-header .name {
+ font-weight: 600;
+ font-size: 14px;
+}
+
+/* Scrollbar default highlight color */
+.node.highlighted {
+ background: #666;
+}
+
+/* Highlighted resource default style (e.g. contributor) */
+.article .resources .info .content-node.highlighted {
+ border-left: 3px solid #212121;
+}
+
+/* Citation resource */
+.lens-article .resources .content-node.citation .resource-header .name {
+ @extend .contextual-label;
+}
+
+.lens-article .resources .content-node.citation {
+ font-family: $prose-font;
+}
+
+.lens-article .content-node.citation .content .authors {
+ font-family: $interface-font;
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.lens-article .content-node.citation .content .source,
+.lens-article .content-node.citation .content .doi {
+ font-size: 11px;
+}
+
+/* Change font-size for all resource nodes */
+.lens-article .resources .figures .nodes > .content-node {
+ font-size: 13px;
+}
+
+/* Caption title (e.g. figure, table) */
+.lens-article .content-node.caption > .content > .content-node.caption-title {
+ font-size: 13px;
+ padding-bottom: 0px;
+}
+
+/* Label for resources */
+.article .resources .resource-header .name {
+ font-size: 13px;
+}
+
+/* HACK: Make sure always the interface font is in toggle buttons (focus, fullscreen) */
+.article .resources .content-node .resource-header .toggles {
+ font-family: $interface-font;
+}
+
+/* Resource view */
+
+.panel.info {
+ font-family: $prose-font;
+}
+
+.lens-article .content-node.publication-info .label {
+ font-family: $interface-font;
+}
+
+.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] .heading .content {
+ font-weight: 600;
+}
+
+.lens-article .content-node.contributor .label {
+ font-family: $interface-font;
+ color: $default-text-color;
+ font-weight: 600;
+}
+
+
+.article .resources .nodes > .content-node.publication-info .content-node[data-id=articleinfo] .heading.level-3 .content {
+ font-family: $interface-font;
+ font-weight: 600;
+}
+
+.lens-article .resources .content-node.contributor .resource-header .name {
+ @extend .contextual-label;
+ font-size: 11px;
+}
+
+.lens-article .content-node.publication-info .label {
+ color: $default-text-color;
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.lens-article .content-node.contributor .contributor-name {
+ font-family: $interface-font;
+ font-weight: 600;
+ font-size: 20px;
+ line-height: 24px;
+ margin-top: 12px;
+ margin-bottom: 12px;
+}
diff --git a/styles/lens.css b/styles/lens.css
index 198d3f24..a49f28d6 100644
--- a/styles/lens.css
+++ b/styles/lens.css
@@ -11,11 +11,10 @@ body {
margin: 0;
background-color: white;
-/* -moz-transition: background-color 200ms linear;
+ /* -moz-transition: background-color 200ms linear;
-o-transition: background-color 200ms linear;
-webkit-transition: background-color 200ms linear;
transition: background-color 200ms linear;*/
-
}
body.loading {
@@ -43,7 +42,7 @@ body.reader {
}
/*
-General Layout
+General Layout
--------------------------------------- */
#container {
@@ -67,7 +66,7 @@ a {
color: #1B6685;
font-weight: normal;
text-decoration: none;
-
+
/* -moz-transition: background-color 100ms linear, color 100ms linear, opacity 100ms linear;
-o-transition: background-color 100ms linear, color 100ms linear, opacity 100ms linear;
-webkit-transition: background-color 100ms linear, color 100ms linear, opacity 100ms linear;
@@ -86,7 +85,7 @@ img {
strong { font-weight: 700; }
-h1, h2, h3 {
+h1, h2, h3 {
font-weight: 700;
}
@@ -94,16 +93,16 @@ h1 a { color: white; }
h1 a:hover { color: white; }
h2 {
- font-size: 1.75em;
+ font-size: 1.75em;
padding-bottom: 20px;
}
-
+
h3, h4, h5, h6 {
margin-bottom: 20px;
font-size: 1em;
font-weight: 700;
}
-
+
p {
padding-bottom: 20px;
}
@@ -154,6 +153,54 @@ p:last-child { padding-bottom: 0; }
color: gold;
}
+.author-callout-style-a1 {
+ color: rgb(54, 107, 251); // Blue
+}
+
+.author-callout-style-a2 {
+ color: rgb(156, 39, 176); // Purple
+}
+
+.author-callout-style-a3 {
+ color: rgb(213, 0, 0); // Red
+}
+
+.author-callout-style-b1 {
+ background-color: rgb(144, 202, 249); // Blue
+}
+
+.author-callout-style-b2 {
+ background-color: rgb(197, 225, 165); // Green
+}
+
+.author-callout-style-b3 {
+ background-color: rgb(255, 183, 77); // Orange
+}
+
+.author-callout-style-b4 {
+ background-color: rgb(255, 241, 118); // Yellow
+}
+
+.author-callout-style-b5 {
+ background-color: rgb(158, 134, 201); // Purple
+}
+
+.author-callout-style-b6 {
+ background-color: rgb(229, 115, 115); // Red
+}
+
+.author-callout-style-b7 {
+ background-color: rgb(244, 143, 177); // Pink
+}
+
+.author-callout-style-b8 {
+ background-color: rgb(230, 230, 230); // Grey
+}
+
+.lens-article .content-node.cover .subjects a:not(:last-child):after {
+ content: ', '
+}
+
/* main
--------------------------------------- */
@@ -195,7 +242,7 @@ body.loading .spinner-wrapper {
.spinner-wrapper .spinner {
width: 40px;
height: 40px;
- margin: 0 auto;
+ margin: 0 auto;
background: #444;
-webkit-animation: rotateplane 1.2s infinite ease-in-out;
animation: rotateplane 1.2s infinite ease-in-out;
diff --git a/styles/reader.css b/styles/reader.css
index 251425e7..7a3a6af7 100644
--- a/styles/reader.css
+++ b/styles/reader.css
@@ -146,7 +146,7 @@ out (on iOS 5.1), or flicker (on iOS 6). */
position: relative;
}
-/* It's not exactly good to have the overflow-y: scroll for the container AND the surface.
+/* It's not exactly good to have the overflow-y: scroll for the container AND the surface.
There should be just one overflowing container, if possible
*/
@@ -169,7 +169,6 @@ out (on iOS 5.1), or flicker (on iOS 6). */
right: 0px;
overflow-y: scroll;
overflow-x: hidden;
- font-size: 14px;
-webkit-overflow-scrolling: touch;
}
@@ -184,7 +183,6 @@ out (on iOS 5.1), or flicker (on iOS 6). */
}
.article .resources .nodes > .content-node {
- color: #505050;
position: relative;
background: #fff;
border-bottom: 1px solid #ddd;
@@ -237,8 +235,6 @@ out (on iOS 5.1), or flicker (on iOS 6). */
.article .resources .resource-header .name {
display: block;
min-height: 30px;
-
- color: #444;
font-size: 16px;
line-height: 21px;
padding: 0px 20px;
@@ -404,7 +400,7 @@ span.annotation.formula_reference, span.publication_reference {
.content-node .question { background-color: rgba(16, 167, 222, 0.3); }
.content-node .error { background-color: rgba(237, 96, 48, 0.3); }
-.content-node .link { color: #1B6685; font-weight: bold; }
+.content-node .link { font-weight: bold; }
.content-node .link:hover, .content-node .link.highlighted { color: rgba(11, 157, 217, 1); }
/* Inline Code Annotations */
@@ -755,8 +751,11 @@ TOC
/* Colors for scroll-bar overlays */
.article .resources .figures .surface-scrollbar .highlighted,
-.article .content .surface-scrollbar .highlighted.figure_reference
+.article .content .surface-scrollbar .highlighted.figure_reference,
+.article .content .surface-scrollbar .highlighted.figure
{ background-color: rgba(145, 187, 4, 1); }
+.article .content .surface-scrollbar .highlighted.figure.main-occurrence
+ { border-right: 3px solid #5C6148; }
.article .resources .citations .surface-scrollbar .highlighted,
.article .content .surface-scrollbar .highlighted.citation_reference
@@ -783,4 +782,40 @@ TOC
.panel.document .nodes > .content-node > .focus-handle:hover {
border-left: 3px solid #bbb;
-}
\ No newline at end of file
+}
+
+
+/* Footnote
+=============================== */
+
+.content-node.footnote {
+ font-size: 13px;
+ display: block !important;
+ margin: 20px 0;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ padding: 20px;
+}
+
+.footnote-reference > a {
+ vertical-align: super;
+ padding: 0 2px;
+ border: 1px solid #ddd;
+ font-size: 13px;
+}
+
+.footnote-reference > a:hover {
+ background: #eee;
+}
+
+.footnote-reference.sm-expanded > a {
+ background: #ddd;
+}
+
+.footnote-reference > .footnote {
+ display: none !important;
+}
+
+.footnote-reference.sm-expanded >.footnote {
+ display: block !important;
+}
diff --git a/substance/document/container.js b/substance/document/container.js
index c139a3c4..8510e9f3 100644
--- a/substance/document/container.js
+++ b/substance/document/container.js
@@ -136,6 +136,10 @@ Container.Prototype = function() {
return this.listView.length;
};
+ this.hasContent = function(panelType) {
+ return this.listView.length > 0 || (panelType === 'resource' && this.treeView.length > 0);
+ };
+
// Returns true if there is another node after a given position.
// --------
//
diff --git a/substance/util/getRelativeBoundingRect.js b/substance/util/getRelativeBoundingRect.js
new file mode 100644
index 00000000..3a8d4186
--- /dev/null
+++ b/substance/util/getRelativeBoundingRect.js
@@ -0,0 +1,95 @@
+'use strict';
+
+var _ = require("underscore");
+var map = _.map;
+var forEach = _.each;
+
+/*
+ Calculate a bounding rectangle for a set of rectangles.
+
+ Note: Here, `bounds.right` and `bounds.bottom` are relative to
+ the left top of the viewport.
+*/
+function _getBoundingRect(rects) {
+ var bounds = {
+ left: Number.POSITIVE_INFINITY,
+ top: Number.POSITIVE_INFINITY,
+ right: Number.NEGATIVE_INFINITY,
+ bottom: Number.NEGATIVE_INFINITY,
+ width: Number.NaN,
+ height: Number.NaN
+ };
+
+ forEach(rects, function(rect) {
+ if (rect.left < bounds.left) {
+ bounds.left = rect.left;
+ }
+ if (rect.top < bounds.top) {
+ bounds.top = rect.top;
+ }
+ if (rect.left + rect.width > bounds.right) {
+ bounds.right = rect.left + rect.width;
+ }
+ if (rect.top + rect.height > bounds.bottom) {
+ bounds.bottom = rect.top + rect.height;
+ }
+ });
+ bounds.width = bounds.right - bounds.left;
+ bounds.height = bounds.bottom - bounds.top;
+ return bounds;
+}
+
+/*
+ Calculate the bounding rect of a single element relative to a parent.
+
+ The rectangle dimensions are calculated as the union of the given elements
+ clientRects. A selection fragment, for example, may appear as a multi-line span
+ element that consists of a single client rect per line of text in variable widths.
+*/
+function _getBoundingOffsetsRect(el, relativeParentEl) {
+ var relativeParentElRect = relativeParentEl.getBoundingClientRect();
+ var elRect = _getBoundingRect(el.getClientRects());
+
+ var left = elRect.left - relativeParentElRect.left;
+ var top = elRect.top - relativeParentElRect.top;
+ return {
+ left: left,
+ top: top,
+ right: relativeParentElRect.width - left - elRect.width,
+ bottom: relativeParentElRect.height - top - elRect.height,
+ width: elRect.width,
+ height: elRect.height
+ };
+}
+
+/**
+ Get bounding rectangle relative to a given parent element. Allows multiple
+ elements being passed (we need this for selections that consist of multiple
+ selection fragments). Takes a relative parent element that is used as a
+ reference point, instead of the browser's viewport.
+
+ @param {Array} els elements to compute the bounding rectangle for
+ @param {DOMElement} containerEl relative parent used as a reference point
+ @return {object} rectangle description with left, top, right, bottom, width and height
+*/
+function getRelativeBoundingRect(els, containerEl) {
+ if (els.length === undefined) {
+ els = [els];
+ }
+ var elRects = map(els, function(el) {
+ return _getBoundingOffsetsRect(el, containerEl);
+ });
+
+ var elsRect = _getBoundingRect(elRects);
+ var containerElRect = containerEl.getBoundingClientRect();
+ return {
+ left: elsRect.left,
+ top: elsRect.top,
+ right: containerElRect.width - elsRect.left - elsRect.width,
+ bottom: containerElRect.height - elsRect.top - elsRect.height,
+ width: elsRect.width,
+ height: elsRect.height
+ };
+}
+
+module.exports = getRelativeBoundingRect;
\ No newline at end of file
|