first commit
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
name: PHP Composer
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress --no-suggest
|
||||
|
||||
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
|
||||
# Docs: https://getcomposer.org/doc/articles/scripts.md
|
||||
|
||||
- name: Run test suite
|
||||
run: composer run-script test
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
filter:
|
||||
excluded_paths:
|
||||
- 'tests/*'
|
||||
build:
|
||||
tests:
|
||||
override:
|
||||
- command: 'mkdir -p build/logs'
|
||||
- command: 'php vendor/bin/phpunit --coverage-clover=build/logs/clover.xml'
|
||||
coverage:
|
||||
file: 'build/logs/clover.xml'
|
||||
format: 'clover'
|
||||
nodes:
|
||||
tests: true
|
||||
analysis:
|
||||
tests:
|
||||
override:
|
||||
- command: phpcs-run ./
|
||||
use_website_config: false
|
||||
- php-scrutinizer-run
|
||||
tools:
|
||||
php_cs_fixer:
|
||||
config: { level: psr2 } # or psr1 if you would just like to get fixes for PSR1
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.2.5 2020-12-06
|
||||
|
||||
* Bugfix for [94](https://github.com/seboettg/citeproc-php/issues/95): Missing space between date parts, when date parts have no affixes
|
||||
|
||||
## 2.2.4 2020-11-22
|
||||
|
||||
* Bugfix for [95](https://github.com/seboettg/citeproc-php/issues/95): Missing space between authors and chapter
|
||||
|
||||
## 2.2.3 2020-10-18
|
||||
|
||||
* Bugfix for an error that occurred when accessing a missing plural form for a label.
|
||||
|
||||
## 2.2.2 2020-09-26
|
||||
|
||||
* Bugfix for [92](https://github.com/seboettg/citeproc-php/issues/92)
|
||||
* Bugfix for [93](https://github.com/seboettg/citeproc-php/issues/93)
|
||||
|
||||
## 2.2.1 2020-09-12
|
||||
|
||||
* Bugfix for issue [82](https://github.com/seboettg/citeproc-php/issues/82)
|
||||
* Bugfix for [84](https://github.com/seboettg/citeproc-php/issues/84)
|
||||
* Merged PR [86](https://github.com/seboettg/citeproc-php/pull/86)
|
||||
* Bugfix for [89](https://github.com/seboettg/citeproc-php/issues/89)
|
||||
|
||||
## 2.2.0 2020-04-04
|
||||
* Compatibility for PHP 7.2, 7.3 and 7.4. This solves the issues [76](https://github.com/seboettg/citeproc-php/issues/76), [78](https://github.com/seboettg/citeproc-php/issues/75), [80](https://github.com/seboettg/citeproc-php/issues/80) and [81](https://github.com/seboettg/citeproc-php/issues/81).
|
||||
* Merged Pull Requests [75](https://github.com/seboettg/citeproc-php/pull/75) and [79](https://github.com/seboettg/citeproc-php/pull/79)
|
||||
|
||||
Thanks to [@kchoong](https://github.com/kchoong) and [@westcomputerconsultancy](https://github.com/westcomputerconsultancy).
|
||||
|
||||
## 2.1.9 2019-11-04
|
||||
* bugfix for [issue 68](https://github.com/seboettg/citeproc-php/issues/68)
|
||||
* bugfix for [issue 69](https://github.com/seboettg/citeproc-php/issues/69)
|
||||
* bugfix for [issue 70](https://github.com/seboettg/citeproc-php/issues/70)
|
||||
* feature/enhancement for [issue 71](https://github.com/seboettg/citeproc-php/issues/71)
|
||||
* refactoring of the code parts for rendering date ranges
|
||||
* redesign/refactoring of constraints (condition handling for choose elements)
|
||||
|
||||
## 2.1.8 - 2019-09-13
|
||||
* bugfix of [PR 66](https://github.com/seboettg/citeproc-php/pull/66)
|
||||
* bugfix for displaced delimiters that appear when in the name list more than one empty entry exists.
|
||||
|
||||
## 2.1.7 - 2019-03-24
|
||||
|
||||
* bugfix of [PR 64](https://github.com/seboettg/citeproc-php/pull/64) Call to a member function getRangeDelimiter() on null
|
||||
* bugfix of [PR 63](https://github.com/seboettg/citeproc-php/pull/63) Don't show "et-al" text for citation witch don't reach et-al number
|
||||
|
||||
## 2.1.6 - 2018-10-13
|
||||
|
||||
* bugfix of [issue 59](https://github.com/seboettg/citeproc-php/issues/59)
|
||||
* bugfix of [issue 60](https://github.com/seboettg/citeproc-php/issues/60)
|
||||
|
||||
## 2.1.5 - 2018-09-23
|
||||
|
||||
* bugfix of [issue 57](https://github.com/seboettg/citeproc-php/issues/57)
|
||||
* bugfix of [issue 58](https://github.com/seboettg/citeproc-php/issues/58)
|
||||
|
||||
## 2.1.4 - 2018-07-30
|
||||
|
||||
* bugfix of [PR 52](https://github.com/seboettg/citeproc-php/pull/52): Fix locale overrides using inactive language
|
||||
* bugfix of [PR 53](https://github.com/seboettg/citeproc-php/pull/53): Guard against unset variable
|
||||
* improvement of [PR 54](https://github.com/seboettg/citeproc-php/pull/54): Add loading of primary dialect
|
||||
|
||||
Thanks to [@jonathonwalz](https://github.com/jonathonwalz) for these Pull Requests.
|
||||
|
||||
## 2.1.3 - 2018-06-15
|
||||
|
||||
* bugfix for issue [50](https://github.com/seboettg/citeproc-php/issues/50): In some cases punctuation in quote did not work.
|
||||
|
||||
## 2.1.2 - 2018-04-18
|
||||
|
||||
* bugfix for issue [49](https://github.com/seboettg/citeproc-php/issues/49): Stylesheets that used the ``text-case="title"`` option, in combination with some in Slavic (or Serbo-Croatian) language, caused errors that destroyed the entire output. This was caused by the capitalization of non-letter characters.
|
||||
|
||||
## 2.1.1 - 2018-02-08
|
||||
* Support for render variables that are using "-short" suffixes, if Text tags have a "form" attribute which is set to "short": ``<text form="short" .../>``. This is used e.g. for abbreviated journal title (container-title-short) and occurred a wrong output in different styles (for example AMA American Medical Association) in previous citeproc-php versions. Have a look at issue [47](https://github.com/seboettg/citeproc-php/issues/47).
|
||||
|
||||
## 2.1.0 - 2017-11-23
|
||||
|
||||
* possibility to filter specific citations independently from CSL input data (inspired by @CarlosCraviotto PR #39). Have a look [here](https://github.com/seboettg/citeproc-php/blob/master/README.md#filter-citations).
|
||||
* possibility to use custom Lambda functions to enrich bibliographies and citations with additional HTML markup. Have a look [here](https://github.com/seboettg/citeproc-php/blob/master/README.md#advanced-usage-of-citeproc-php).
|
||||
|
||||
## 2.0.4 - 2017-11-15
|
||||
|
||||
* bugfix for issue [46](https://github.com/seboettg/citeproc-php/issues/46): initialize names didn't work for cyrillic characters
|
||||
|
||||
|
||||
## 2.0.3 - 2017-11-11
|
||||
|
||||
* bugfix for issue [44](https://github.com/seboettg/citeproc-php/issues/44): Missing title with chicago-fullnote-bibliography. The problem occurred because of an incorrect implementation of the "none-condition" in ChooseIf.
|
||||
* bugfix for an issue that appears sometimes in connection with date-parts.
|
||||
|
||||
## 2.0.2 - 2017-10-04
|
||||
|
||||
* bugfix for issue [41](https://github.com/seboettg/citeproc-php/issues/41): fixed missing suppression of substituted values
|
||||
* bugfix for issue [42](https://github.com/seboettg/citeproc-php/issues/42): citeproc-php caused a fatal error if php 5.6 was used
|
||||
* citeproc-php uses now version 1.2 of [seboettg/collection](https://packagist.org/packages/seboettg/collection)
|
||||
|
||||
## 2.0.1 - 2017-05-23
|
||||
|
||||
* bugfix for issue [36](https://github.com/seboettg/citeproc-php/issues/36).
|
||||
* bugfix for issue [37](https://github.com/seboettg/citeproc-php/issues/37).
|
||||
* solves a problem of exceeded script runtime which sometimes occurs while running `composer update`. Now,
|
||||
the depended citation styles and locales are not longer composer dependencies but will be cloned by a shell script instead which simply will triggered from `composer update`.
|
||||
|
||||
## 2.0.0 - 2017-05-11
|
||||
|
||||
* 1st stable release
|
||||
* fix issues that causing if styles using uncertain dates
|
||||
* add info node parser and getInfo() method in Context class. Thus, it's now possible to get meta data of given stylesheet
|
||||
* fix issues in et-al abbreviation which was appearing in citation
|
||||
|
||||
## 2.0.0-beta 2017-05-01
|
||||
|
||||
* beta release
|
||||
* all features of milestone "Version 2.0" have been implemented
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
## Contribution ##
|
||||
|
||||
citeproc-php is an Open Source project. You can support it by reporting bugs, contributing code or contributing documentation.
|
||||
|
||||
### Star the Repo ###
|
||||
Developing software is a hard job and one has to spend a lot of time. Every open-source developer is looking forward
|
||||
about esteem for his work. If you use citeproc-php and if you like it, star it and talk about it in Blogs.
|
||||
|
||||
### Reporting a Bug ###
|
||||
Use the [Issue Tracker](https://github.com/seboettg/citeproc-php/issues) in order to report a bug.
|
||||
|
||||
### Contribute Code ###
|
||||
You are a developer and you like to help developing new features or bug fixes? Fork citeproc-php, setup a workspace and send
|
||||
a pull request.
|
||||
|
||||
I would suggest the following way:
|
||||
|
||||
* Fork citeproc-php on Github
|
||||
* Clone the forked repo
|
||||
```bash
|
||||
$ git clone https://github.com/<yourname>/citeproc-php
|
||||
```
|
||||
* Setup your preferred IDE
|
||||
* create a new branch in your forked repo (do not commit your changes in the master branch)
|
||||
* implement your fix or feature
|
||||
* Run the UnitTests
|
||||
* Write a test case for your issue. My tests are based on the original [test-suite](https://github.com/citation-style-language/test-suite). You can build custom (human-readable) test cases following the described [Fixture layout](https://github.com/citation-style-language/test-suite#fixture-layout).
|
||||
* Additionally, you have to translate (human-readable) test-cases into json format (machine-readable)
|
||||
```bash
|
||||
$ cd <project-root>/tests/fixtures/basic-tests
|
||||
$ ./processor.py -g
|
||||
```
|
||||
* create a test function within an already existing test class or create a new test class:
|
||||
```php
|
||||
<?php
|
||||
namespace Seboettg\CiteProc;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MyNewClassTest extends TestCase
|
||||
{
|
||||
use TestSuiteTestCaseTrait;
|
||||
// ...
|
||||
public function testMyBrandNewFunction()
|
||||
{
|
||||
//my brand new function is the file name (without file extension)
|
||||
$this->_testRenderTestSuite("myBrandNewFunction");
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
* Make sure that your test case covers relevant code parts
|
||||
* Implement the code until all tests finishing successfully
|
||||
* Send a pull request as follows:
|
||||
1. create a new pull request and select the forked repo and the new branch as head
|
||||
2. select the "seboettg/citeproc-php" and the master branch as base
|
||||
3. select "Allow edits by maintainers"
|
||||
4. add title and a meaningful description for your PR
|
||||
5. submit the pull request
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Please follow the general troubleshooting steps first:
|
||||
|
||||
- [ ] I read the README and followed the instructions.
|
||||
- [ ] I am sure that the used CSL metadata follows the [CSL schema](https://github.com/citation-style-language/schema/blob/master/csl-data.json).
|
||||
- [ ] I use a valid CSL stylesheet
|
||||
|
||||
### Bug reports:
|
||||
|
||||
Please replace this line with a brief summary of your issue
|
||||
|
||||
#### Used CSL stylesheet: ####
|
||||
|
||||
Please replace this line with the name of your used CSL stylesheet (i.e. chicago-author-date-16th-edition.csl)
|
||||
|
||||
#### Used CSL metadata ####
|
||||
|
||||
Please replace these lines with your used metadata, for instance:
|
||||
|
||||
[
|
||||
{
|
||||
"author": [
|
||||
{
|
||||
"family": "Anderson",
|
||||
"given": "John"
|
||||
},
|
||||
{
|
||||
"family": "Brown",
|
||||
"given": "John"
|
||||
}
|
||||
],
|
||||
"id": "ITEM-2",
|
||||
"type": "book",
|
||||
"title": "Two authors writing a book"
|
||||
}
|
||||
]
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Sebastian Böttger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
# citeproc-php #
|
||||
[](https://packagist.org/packages/seboettg/citeproc-php)
|
||||
[](https://packagist.org/packages/seboettg/citeproc-php/stats)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://scrutinizer-ci.com/g/seboettg/citeproc-php/build-status/master)
|
||||
[](https://scrutinizer-ci.com/g/seboettg/citeproc-php/code-structure/master/code-coverage/src/)
|
||||
[](https://scrutinizer-ci.com/g/seboettg/citeproc-php/?branch=master)
|
||||
[](https://scrutinizer-ci.com/code-intelligence)
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
citeproc-php is a full-featured CSL 1.0.1 processor that renders bibliographic metadata into html formatted citations or bibliographies using CSL stylesheets. citeproc-php renders bibliographies as well as citations (except of [Citation-specific Options](http://docs.citationstyles.org/en/stable/specification.html#citation-specific-options)).
|
||||
|
||||
## Citation Style Language CSL ##
|
||||
|
||||
The Citation Style Language (CSL) is an XML-based format to describe the formatting of citations, notes and bibliographies, offering:
|
||||
|
||||
* An open format
|
||||
* Compact and robust styles
|
||||
* Extensive support for style requirements
|
||||
* Automatic style localization
|
||||
* Infrastructure for style distribution and updating
|
||||
* Thousands of freely available styles (Creative Commons BY-SA licensed)
|
||||
|
||||
For additional documentation of CSL visit [http://citationstyles.org](http://citationstyles.org).
|
||||
|
||||
## Installing citeproc-php ##
|
||||
|
||||
The recommended way to install citeproc-php is through
|
||||
[Composer](https://getcomposer.org).
|
||||
|
||||
```bash
|
||||
$ curl -sS https://getcomposer.org/installer | php
|
||||
```
|
||||
Add the following lines to your `composer.json` file in order to add required program libraries as well as CSL styles and locales:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "vendor-name/program-name",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "citation-style-language/locales",
|
||||
"version":"1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/citation-style-language/locales.git",
|
||||
"reference": "master"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "citation-style-language/styles",
|
||||
"version":"1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/citation-style-language/styles.git",
|
||||
"reference": "master"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"citation-style-language/locales":"@dev",
|
||||
"citation-style-language/styles":"@dev",
|
||||
"seboettg/citeproc-php": "^2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, run the Composer command to install the latest stable version of citeproc-php and its dependencies:
|
||||
|
||||
```bash
|
||||
$ php composer.phar install --no-dev
|
||||
```
|
||||
|
||||
After installing, you need to require Composer's autoloader:
|
||||
|
||||
```php
|
||||
require 'vendor/autoload.php';
|
||||
```
|
||||
|
||||
You can then later update citeproc-php using composer:
|
||||
|
||||
```bash
|
||||
$ composer.phar update --no-dev
|
||||
```
|
||||
|
||||
If you have trouble using composer you will find further information on [https://getcomposer.org/doc/](https://getcomposer.org/doc/).
|
||||
|
||||
## How to use citeproc-php ##
|
||||
|
||||
citeproc-php renders bibliographical metadata into html formatted citations or bibliographies using a stylesheet which defines the
|
||||
citation rules.
|
||||
|
||||
|
||||
### Get the metadata of your publications ###
|
||||
|
||||
Create a project folder:
|
||||
|
||||
```bash
|
||||
$ mkdir mycslproject
|
||||
$ cd mycslproject
|
||||
```
|
||||
|
||||
First, you need json formatted metadata array of publication's metadata. There are a lot of services that supports CSL exports. For instance [BibSonomy](https://www.bibsonomy.org), [Zotero](https://www.zotero.org/), [Mendeley](https://www.mendeley.com/).
|
||||
If you don't use any of these services, you can use the following test data for a first step.
|
||||
|
||||
```javascript
|
||||
[
|
||||
{
|
||||
"author": [
|
||||
{
|
||||
"family": "Doe",
|
||||
"given": "James",
|
||||
"suffix": "III"
|
||||
}
|
||||
],
|
||||
"id": "item-1",
|
||||
"issued": {
|
||||
"date-parts": [
|
||||
[
|
||||
"2001"
|
||||
]
|
||||
]
|
||||
},
|
||||
"title": "My Anonymous Heritage",
|
||||
"type": "book"
|
||||
},
|
||||
{
|
||||
"author": [
|
||||
{
|
||||
"family": "Anderson",
|
||||
"given": "John"
|
||||
},
|
||||
{
|
||||
"family": "Brown",
|
||||
"given": "John"
|
||||
}
|
||||
],
|
||||
"id": "ITEM-2",
|
||||
"type": "book",
|
||||
"title": "Two authors writing a book"
|
||||
}
|
||||
]
|
||||
```
|
||||
Copy this into a file in your project root and name that file `metadata.json`.
|
||||
|
||||
### Build a first simple script ###
|
||||
|
||||
```php
|
||||
<?php
|
||||
include "vendor/autoload.php";
|
||||
use Seboettg\CiteProc\StyleSheet;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
|
||||
$data = file_get_contents("metadata.json");
|
||||
$style = StyleSheet::loadStyleSheet("din-1505-2");
|
||||
$citeProc = new CiteProc($style);
|
||||
echo $citeProc->render(json_decode($data), "bibliography");
|
||||
```
|
||||
|
||||
You can also render citations instead of bibliographies:
|
||||
|
||||
```php
|
||||
echo $citeProc->render(json_decode($data), "citation");
|
||||
```
|
||||
|
||||
### Filter Citations ###
|
||||
|
||||
Since version 2.1 you have also the possibility to apply a filter so that just specific citations appear.
|
||||
|
||||
```php
|
||||
<p>This a wise sentence
|
||||
<?php echo $citeProc->render($data, "citation", json_decode('[{"id":"item-1"}]')); ?>.</p>
|
||||
<p>This is the most wise sentence
|
||||
<?php echo $citeProc->render($data, "citation", json_decode('[{"id":"item-1"},{"id":"ITEM-2"}]')); ?>.</p>
|
||||
```
|
||||
|
||||
### Bibliography-specific styles using CSS ###
|
||||
|
||||
Some CSL stylesheets use bibliography-specific style options like hanging indents or alignments. To get an effect of these options you can render separated Cascading Stylesheets using CiteProc.
|
||||
You have to insert these styles within the `<head>` tag of your html output page.
|
||||
|
||||
```php
|
||||
<?php
|
||||
include "vendor/autoload.php";
|
||||
use Seboettg\CiteProc\StyleSheet;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
|
||||
$data = file_get_contents("metadata.json");
|
||||
$style = StyleSheet::loadStyleSheet("harvard-north-west-university");
|
||||
$citeProc = new CiteProc($style);
|
||||
$bibliography = $citeProc->render(json_decode($data), "bibliography");
|
||||
$cssStyles = $citeProc->renderCssStyles();
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSL Test</title>
|
||||
<style type="text/css" rel="stylesheet">
|
||||
<?php echo $cssStyles; ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bibliography</h1>
|
||||
<?php echo $bibliography; ?>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Now, you can watch and test the output using PHP's internal web server:
|
||||
|
||||
```bash
|
||||
$ php -S localhost:8080
|
||||
```
|
||||
|
||||
Start your Browser and open the URL `http://localhost:8080`.
|
||||
|
||||
Under `examples` folder you will find another example script.
|
||||
|
||||
|
||||
## Advanced usage of citeproc-php ##
|
||||
|
||||
Since version 2.1, citeproc-php comes with additional features that are not a part of the CSL specifications.
|
||||
|
||||
You can enrich bibliographies and citations with additional HTML tags to inject links (i.e. to set a link to an author's CV), or to add other html markup.
|
||||
|
||||
### Use Lambda Functions to setup citeproc-php in order to render advanced citations or bibliographies ###
|
||||
|
||||
```php
|
||||
<?php
|
||||
include "vendor/autoload.php";
|
||||
use Seboettg\CiteProc\StyleSheet;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
|
||||
$data = file_get_contents("metadata.json");
|
||||
$style = StyleSheet::loadStyleSheet("elsevier-vancouver");
|
||||
|
||||
// pimp the title
|
||||
$titleFunction = function($cslItem, $renderedText) {
|
||||
return '<a href="https://example.org/publication/' . $cslItem->id . '">' . $renderedText . '</a>';
|
||||
};
|
||||
|
||||
//pimp author names
|
||||
$authorFunction = function($authorItem, $renderedText) {
|
||||
if (isset($authorItem->id)) {
|
||||
return '<a href="https://example.org/author/' . $authorItem->id . '">' . $renderedText . '</a>';
|
||||
}
|
||||
return $renderedText;
|
||||
};
|
||||
?>
|
||||
```
|
||||
|
||||
As you can see, `$titleFunction` wraps the title and `$authorFunction` wraps author's name in a link.
|
||||
|
||||
Assign these functions to its associated CSL variable (in this case title and author) as follows.
|
||||
|
||||
```php
|
||||
<?php
|
||||
$additionalMarkup = [
|
||||
"title" => $titleFunction,
|
||||
"author" => $authorFunction
|
||||
];
|
||||
|
||||
$citeProc = new CiteProc($style, "en-US", $additionalMarkup);
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSL Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bibliography</h1>
|
||||
<?php echo $citeProc->render(json_decode($data), "bibliography"); ?>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
You can also use custom Lambda Functions in order to enrich citations with additional HTML markup.
|
||||
|
||||
If you want to restrict citeproc-php to use a custom Lambda Function either for bibliographies or citations, or you want to apply different
|
||||
functions for both, you can define the array as follows:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$additionalMarkup = [
|
||||
"bibliography" => [
|
||||
"author" => $authorFunction,
|
||||
"title" => $titleFunction,
|
||||
"csl-entry" => function($cslItem, $renderedText) {
|
||||
return '<a id="' . $cslItem->id .'" href="#' . $cslItem->id .'"></a>' . $renderedText;
|
||||
}
|
||||
],
|
||||
"citation" => [
|
||||
"citation-number" => function($cslItem, $renderedText) {
|
||||
return '<a href="#' . $cslItem->id .'">'.$renderedText.'</a>';
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
$citeProc = new CiteProc($style, "en-US", $additionalMarkup);
|
||||
|
||||
?>
|
||||
<p>This ia a wise sentence <?php echo $citeProc->render(json_decode($data), "citation", json_decode('[{"id":"item-1"}]')); ?>.</p>
|
||||
<h3>Literature</h3>
|
||||
<?php echo $citeProc->render(json_decode($data), "bibliography");
|
||||
|
||||
```
|
||||
In this example each entry of the bibliography gets an anchor by its `id` and the citation (in Elsevier-Vancouver style [1]) gets an URL with a fragment by its `id`. Hence, every citation mark gets a link to its entry in the bibliography.
|
||||
Further examples you will find in the example folder.
|
||||
|
||||
### Good to know ###
|
||||
* A custom Lambda Function must have two parameters (`function ($item, $renderedValue) { ... }`) in their signature and must return a string.
|
||||
* The 1st parameter of a custom Lambda Function is the item (either a citation item or a name item. Both of type `\stdClass`). The 2nd parameter is the rendered result of the associated item.
|
||||
* Custom Lambda Functions may be applied on all Standard Variables (according to the [CSL specification](http://docs.citationstyles.org/en/1.0.1/specification.html#standard-variables)).
|
||||
* Custom Lambda Functions may be applied on all Name Variables (according to the [CSL specification](http://docs.citationstyles.org/en/1.0.1/specification.html#name-variables)). Be aware, just one name item will passed as parameter instead of the full citation item.
|
||||
* Custom Lambda Function for Number Variables or Date Variables will be ignored.
|
||||
* ```csl-entry``` is not a valid variable according to the CSL specifications. citeproc-php use ```csl-entry``` to hook in and apply a custom Lambda Function after a whole citation item or bibliography entry is rendered.
|
||||
|
||||
## Contribution ##
|
||||
|
||||
citeproc-php is an Open Source project. You can support it by reporting bugs, contributing code or contributing documentation.
|
||||
|
||||
### Star the Repo ###
|
||||
Developing software is a hard job and one has to spend a lot of time. Every open-source developer is looking forward
|
||||
about esteem for his work. If you use citeproc-php and if you like it, star it and talk about it in Blogs.
|
||||
|
||||
### Reporting a Bug ###
|
||||
Use the [Issue Tracker](https://github.com/seboettg/citeproc-php/issues) in order to report a bug.
|
||||
|
||||
### Contribute Code ###
|
||||
You are a developer and you like to help developing new features or bug fixes? Fork citeproc-php, setup a workspace and send
|
||||
a pull request.
|
||||
|
||||
I would suggest the following way:
|
||||
|
||||
* Fork citeproc-php on Github
|
||||
* Clone the forked repo
|
||||
```bash
|
||||
$ git clone https://github.com/<yourname>/citeproc-php
|
||||
```
|
||||
* Setup your preferred IDE
|
||||
* Run the UnitTests within your IDE
|
||||
* Write a test case for your issue. My tests are based on the original [test-suite](https://github.com/citation-style-language/test-suite). You can build custom (human-readable) test cases following the described [Fixture layout](https://github.com/citation-style-language/test-suite#fixture-layout).
|
||||
* Additionally, you have to translate (human-readable) test-cases into json format (machine-readable)
|
||||
```bash
|
||||
$ cd <project-root>/tests/fixtures/basic-tests
|
||||
$ ./processor.py -g
|
||||
```
|
||||
* create a test function within an already existing test class or create a new test class:
|
||||
```php
|
||||
<?php
|
||||
namespace Seboettg\CiteProc;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MyNewClassTest extends TestCase
|
||||
{
|
||||
use TestSuiteTestCaseTrait;
|
||||
// ...
|
||||
public function testMyBrandNewFunction()
|
||||
{
|
||||
//my brand new function is the file name (without file extension)
|
||||
$this->_testRenderTestSuite("myBrandNewFunction");
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
* Implement or adapt your code as long as all tests finishing successfully
|
||||
* Make sure that your test case covers relevant code parts
|
||||
* Send a pull request
|
||||
|
||||
## Testing ##
|
||||
|
||||
You can also run test cases without IDE:
|
||||
|
||||
```bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
## Known projects that use citeproc-php
|
||||
|
||||
* [Citation Style Language plugin for Open Journal Systems 3](https://github.com/pkp/citationStyleLanguage)
|
||||
* [Islandora Scholar](https://github.com/Islandora/islandora_scholar)
|
||||
* [Grav Bibliography Plugin](https://github.com/OleVik/grav-plugin-bibliography)
|
||||
* [Bibcite Wordpress Plugin](https://github.com/ShadyChars/Bibcite)
|
||||
* [DKAN Science Metadata](https://github.com/GetDKAN/dkan_sci_metadata)
|
||||
* [Stud.IP](https://github.com/studip/studip)
|
||||
* [bibliography Plugin for DokuWiki](https://github.com/gyger/dokuwiki-bibliography)
|
||||
* [ldbase_citations (Plugin for LDbase)](https://github.com/ldbase/ldbase_citations)
|
||||
* [Citations - (Drupal module)](https://github.com/fsulib/citations)
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "seboettg/citeproc-php",
|
||||
"description": "Full-featured CSL processor (https://citationstyles.org)",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Böttger",
|
||||
"email": "seboettg@gmail.com",
|
||||
"homepage": "https://sebastianboettger.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/seboettg/citeproc-php/issues"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Seboettg\\CiteProc\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Seboettg\\CiteProc\\Test\\": "tests/src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"seboettg/collection": ">=v3.1.0",
|
||||
"myclabs/php-enum": "^1.8",
|
||||
"ext-simplexml": "*",
|
||||
"ext-json": "*",
|
||||
"php": ">=7.3",
|
||||
"ext-mbstring": "*",
|
||||
"ext-intl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^1",
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"phpmd/phpmd": "^2.8"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/polyfill-mbstring": "^1.10"
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"./install.sh styles",
|
||||
"./install.sh locales",
|
||||
"@compile-test-cases",
|
||||
"chmod +x vendor/bin/phpunit"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"./install.sh styles",
|
||||
"./install.sh locales",
|
||||
"@compile-test-cases",
|
||||
"chmod +x vendor/bin/phpunit"
|
||||
],
|
||||
"test": "vendor/bin/phpunit -c phpunit.xml",
|
||||
"check": [
|
||||
"@cs-check",
|
||||
"@test"
|
||||
],
|
||||
"cs-check": "phpcs",
|
||||
"cs-fix": "phpcbf",
|
||||
"compile-test-cases": "cd ./tests/fixtures/basic-tests/; ./processor.py -g"
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -d ./vendor/citation-style-language/$1 ]
|
||||
then
|
||||
cd ./vendor/citation-style-language/$1
|
||||
git pull origin master
|
||||
else
|
||||
git clone --branch=master https://github.com/citation-style-language/$1.git vendor/citation-style-language/$1
|
||||
fi
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="citeproc-php coding standard">
|
||||
<description>citeproc-php coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- inherit rules from: -->
|
||||
<rule ref="PSR2"/>
|
||||
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
|
||||
<properties>
|
||||
<property name="ignoreBlankLines" value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>src</file>
|
||||
</ruleset>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="citeproc-php coding standard">
|
||||
<description>citeproc-php coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- inherit rules from: -->
|
||||
<rule ref="PSR2"/>
|
||||
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
|
||||
<properties>
|
||||
<property name="ignoreBlankLines" value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>src</file>
|
||||
</ruleset>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ citeproc-php
|
||||
~
|
||||
~ @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
~ @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
~ @license https://opensource.org/licenses/MIT
|
||||
-->
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="tests/bootstrap.php">
|
||||
<php>
|
||||
<!-- -->
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Project Test Suite">
|
||||
<directory>tests/src</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./src</directory>
|
||||
<exclude>
|
||||
<directory>./vendor</directory>
|
||||
<directory>./tests</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-html" target="review/code-coverage"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Root\Info;
|
||||
use Seboettg\CiteProc\Style\Bibliography;
|
||||
use Seboettg\CiteProc\Style\Citation;
|
||||
use Seboettg\CiteProc\Style\Macro;
|
||||
use Seboettg\CiteProc\Style\Options\GlobalOptions;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use Seboettg\CiteProc\Styles\Css\CssStyle;
|
||||
use Seboettg\CiteProc\Util\CiteProcHelper;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class CiteProc
|
||||
* @package Seboettg\CiteProc
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CiteProc
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
private static $context;
|
||||
|
||||
|
||||
/**
|
||||
* @return Context
|
||||
*/
|
||||
public static function getContext()
|
||||
{
|
||||
return self::$context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Context $context
|
||||
*/
|
||||
public static function setContext($context)
|
||||
{
|
||||
self::$context = $context;
|
||||
}
|
||||
|
||||
private $lang;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $styleSheet;
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement
|
||||
*/
|
||||
private $styleSheetXml;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $markupExtension;
|
||||
|
||||
/**
|
||||
* CiteProc constructor.
|
||||
* @param string $styleSheet xml formatted csl stylesheet
|
||||
* @param string $lang
|
||||
* @param array $markupExtension
|
||||
*/
|
||||
public function __construct($styleSheet, $lang = "en-US", $markupExtension = [])
|
||||
{
|
||||
$this->styleSheet = $styleSheet;
|
||||
$this->lang = $lang;
|
||||
$this->markupExtension = $markupExtension;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
self::$context = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $style
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function parse(SimpleXMLElement $style)
|
||||
{
|
||||
$root = new Root();
|
||||
$root->initInheritableNameAttributes($style);
|
||||
self::$context->setRoot($root);
|
||||
$globalOptions = new GlobalOptions($style);
|
||||
self::$context->setGlobalOptions($globalOptions);
|
||||
|
||||
/** @var SimpleXMLElement $node */
|
||||
foreach ($style as $node) {
|
||||
$name = $node->getName();
|
||||
switch ($name) {
|
||||
case 'info':
|
||||
self::$context->setInfo(new Info($node));
|
||||
break;
|
||||
case 'locale':
|
||||
self::$context->getLocale()->addXml($node);
|
||||
break;
|
||||
case 'macro':
|
||||
$macro = new Macro($node, $root);
|
||||
self::$context->addMacro($macro->getName(), $macro);
|
||||
break;
|
||||
case 'bibliography':
|
||||
$bibliography = new Bibliography($node, $root);
|
||||
self::$context->setBibliography($bibliography);
|
||||
break;
|
||||
case 'citation':
|
||||
$citation = new Citation($node, $root);
|
||||
self::$context->setCitation($citation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataList $data
|
||||
* @return string
|
||||
*/
|
||||
protected function bibliography($data)
|
||||
{
|
||||
|
||||
return self::$context->getBibliography()->render($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataList $data
|
||||
* @param ArrayList $citationItems
|
||||
* @return string
|
||||
*/
|
||||
protected function citation($data, $citationItems)
|
||||
{
|
||||
return self::$context->getCitation()->render($data, $citationItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param string $mode (citation|bibliography)
|
||||
* @param array $citationItems
|
||||
* @param bool $citationAsArray
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function render($data, $mode = "bibliography", $citationItems = [], $citationAsArray = false)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$data = CiteProcHelper::cloneArray($data);
|
||||
}
|
||||
|
||||
if (!in_array($mode, ['citation', 'bibliography'])) {
|
||||
throw new InvalidArgumentException("\"$mode\" is not a valid mode.");
|
||||
}
|
||||
|
||||
$this->init($citationAsArray); //initialize
|
||||
|
||||
$res = "";
|
||||
|
||||
if (is_array($data)) {
|
||||
$data = new DataList(...$data);
|
||||
} elseif (!($data instanceof DataList)) {
|
||||
throw new CiteProcException('No valid format for variable data. Either DataList or array expected');
|
||||
}
|
||||
|
||||
switch ($mode) {
|
||||
case 'bibliography':
|
||||
self::$context->setMode($mode);
|
||||
// set CitationItems to Context
|
||||
self::getContext()->setCitationData($data);
|
||||
$res = $this->bibliography($data);
|
||||
break;
|
||||
case 'citation':
|
||||
if (is_array($citationItems)) {
|
||||
$citationItems = new ArrayList(...$citationItems);
|
||||
} elseif (!($citationItems instanceof ArrayList)) {
|
||||
throw new CiteProcException('No valid format for variable `citationItems`, ArrayList expected.');
|
||||
}
|
||||
self::$context->setMode($mode);
|
||||
// set CitationItems to Context
|
||||
self::getContext()->setCitationItems($citationItems);
|
||||
$res = $this->citation($data, $citationItems);
|
||||
}
|
||||
self::setContext(null);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes CiteProc and start parsing XML stylesheet
|
||||
* @param bool $citationAsArray
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function init($citationAsArray = false)
|
||||
{
|
||||
self::$context = new Context();
|
||||
self::$context->setLocale(new Locale\Locale($this->lang)); //init locale
|
||||
self::$context->setCitationsAsArray($citationAsArray);
|
||||
// set markup extensions
|
||||
self::$context->setMarkupExtension($this->markupExtension);
|
||||
$this->styleSheetXml = new SimpleXMLElement($this->styleSheet);
|
||||
$this->parse($this->styleSheetXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function renderCssStyles()
|
||||
{
|
||||
if (self::getContext() === null) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
if (self::getContext()->getCssStyle() == null) {
|
||||
$cssStyle = new CssStyle(self::getContext()->getBibliographySpecificOptions());
|
||||
self::getContext()->setCssStyle($cssStyle);
|
||||
}
|
||||
|
||||
return self::getContext()->getCssStyle()->render();
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2019 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use stdClass;
|
||||
|
||||
abstract class AbstractConstraint implements Constraint
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $match;
|
||||
|
||||
/**
|
||||
* @var ArrayList\ArrayListInterface
|
||||
*/
|
||||
protected $conditionVariables;
|
||||
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param stdClass $data;
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function matchForVariable(string $variable, stdClass $data): bool;
|
||||
|
||||
/**
|
||||
* Variable constructor.
|
||||
* @param string $variableValues
|
||||
* @param string $match
|
||||
*/
|
||||
public function __construct(string $variableValues, string $match = "any")
|
||||
{
|
||||
$this->conditionVariables = new ArrayList(...explode(" ", $variableValues));
|
||||
$this->match = $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(stdClass $data, int $citationNumber = null): bool
|
||||
{
|
||||
switch ($this->match) {
|
||||
case Constraint::MATCH_ALL:
|
||||
return $this->matchAll($data);
|
||||
case Constraint::MATCH_NONE:
|
||||
return $this->matchNone($data); //no match for any value
|
||||
case Constraint::MATCH_ANY:
|
||||
default:
|
||||
return $this->matchAny($data);
|
||||
}
|
||||
}
|
||||
|
||||
private function matchAny(stdClass $data): bool
|
||||
{
|
||||
return $this->conditionVariables
|
||||
->map(function (string $conditionVariable) use ($data) {
|
||||
return $this->matchForVariable($conditionVariable, $data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === true;
|
||||
})
|
||||
->count() > 0;
|
||||
}
|
||||
|
||||
private function matchAll(stdClass $data): bool
|
||||
{
|
||||
return $this->conditionVariables
|
||||
->map(function (string $conditionVariable) use ($data) {
|
||||
return $this->matchForVariable($conditionVariable, $data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === true;
|
||||
})
|
||||
->count() === $this->conditionVariables->count();
|
||||
}
|
||||
|
||||
private function matchNone(stdClass $data): bool
|
||||
{
|
||||
return $this->conditionVariables
|
||||
->map(function (string $conditionVariable) use ($data) {
|
||||
return $this->matchForVariable($conditionVariable, $data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === false;
|
||||
})
|
||||
->count() === $this->conditionVariables->count();
|
||||
}
|
||||
}
|
||||
plugins/generic/citationStyleLanguage/lib/vendor/seboettg/citeproc-php/src/Constraint/Constraint.php
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Interface ConstraintInterface
|
||||
* @package Seboettg\CiteProc\Constraint
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
/** @noinspection PhpUnused */
|
||||
interface Constraint
|
||||
{
|
||||
const MATCH_NONE = "none";
|
||||
|
||||
const MATCH_ANY = "any";
|
||||
|
||||
const MATCH_ALL = "all";
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(stdClass $data, int $citationNumber = null): bool;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Disambiguate
|
||||
* When set to “true” (the only allowed value), the element content is only rendered if it disambiguates two otherwise
|
||||
* identical citations. This attempt at disambiguation is only made when all other disambiguation methods have failed
|
||||
* to uniquely identify the target source.
|
||||
*/
|
||||
class Disambiguate implements Constraint
|
||||
{
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(stdClass $data, $citationNumber = null): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use Seboettg\CiteProc\Exception\ClassNotFoundException;
|
||||
use function Seboettg\CiteProc\ucfirst;
|
||||
|
||||
class Factory extends \Seboettg\CiteProc\Util\Factory
|
||||
{
|
||||
const NAMESPACE_CONSTRAINTS = "Seboettg\\CiteProc\\Constraint\\";
|
||||
|
||||
/**
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function createConstraint(string $name, string $value, string $match): Constraint
|
||||
{
|
||||
$parts = explode("-", $name);
|
||||
$className = implode("", array_map(function ($part) {
|
||||
return ucfirst($part);//overridden function
|
||||
}, $parts));
|
||||
$className = self::NAMESPACE_CONSTRAINTS . $className;
|
||||
|
||||
if (!class_exists($className)) {
|
||||
throw new ClassNotFoundException($className);
|
||||
}
|
||||
return new $className($value, $match);
|
||||
}
|
||||
}
|
||||
Vendored
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use NumberFormatter;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Util\NumberHelper;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class IsNumeric
|
||||
* @package Seboettg\CiteProc\Choose\Constraint
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
/** @noinspection PhpUnused */
|
||||
class IsNumeric extends AbstractConstraint
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param stdClass $data
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchForVariable(string $variable, stdClass $data): bool
|
||||
{
|
||||
if (isset($data->{$variable})) {
|
||||
return $this->parseValue($data->{$variable});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the given variables (Appendix IV - Variables) contain numeric content. Content is considered
|
||||
* numeric if it solely consists of numbers. Numbers may have prefixes and suffixes (“D2”, “2b”, “L2d”), and may be
|
||||
* separated by a comma, hyphen, or ampersand, with or without spaces (“2, 3”, “2-4”, “2 & 4”). For example, “2nd”
|
||||
* tests “true” whereas “second” and “2nd edition” test “false”.
|
||||
*
|
||||
* @param $evalValue
|
||||
* @return bool
|
||||
*/
|
||||
private function parseValue($evalValue): bool
|
||||
{
|
||||
if (is_numeric($evalValue)) {
|
||||
return true;
|
||||
} elseif (preg_match(NumberHelper::PATTERN_ORDINAL, $evalValue)) {
|
||||
$numberFormatter = new NumberFormatter(
|
||||
CiteProc::getContext()->getLocale()->getLanguage(),
|
||||
NumberFormatter::ORDINAL
|
||||
);
|
||||
return $numberFormatter->parse($evalValue) !== false;
|
||||
} elseif (preg_match(NumberHelper::PATTERN_ROMAN, $evalValue)) {
|
||||
return NumberHelper::roman2Dec($evalValue) !== false;
|
||||
} elseif (preg_match(NumberHelper::PATTERN_COMMA_AMPERSAND_RANGE, $evalValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class isUncertainDate
|
||||
* Tests whether the given date variables contain approximate dates.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Constraint
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
/** @noinspection PhpUnused */
|
||||
class IsUncertainDate extends AbstractConstraint
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param stdClass $data ;
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchForVariable(string $variable, stdClass $data): bool
|
||||
{
|
||||
if (!empty($data->{$variable})) {
|
||||
if (isset($data->{$variable}->{'circa'})) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Jurisdiction
|
||||
* @package Seboettg\CiteProc\Constraint
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Jurisdiction implements Constraint
|
||||
{
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(stdClass $data, int $citationNumber = null): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Locator
|
||||
*
|
||||
* Tests whether the locator matches the given locator types (see Locators). Use “sub-verbo” to test for the
|
||||
* “sub verbo” locator type.
|
||||
*/
|
||||
class Locator extends AbstractConstraint
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function matchForVariable(string $variable, stdClass $data): bool
|
||||
{
|
||||
if (!empty($data->id)) {
|
||||
$citationItem = CiteProc::getContext()->getCitationItemById($data->id);
|
||||
return !empty($citationItem) && !empty($citationItem->label) && $citationItem->label === $variable;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Position
|
||||
*
|
||||
* Tests whether the cite position matches the given positions (terminology: citations consist of one or more cites to
|
||||
* individual items). When called within the scope of cs:bibliography, position tests “false”. The positions that can
|
||||
* be tested are:
|
||||
* - “first”: position of cites that are the first to reference an item
|
||||
* - “ibid”/”ibid-with-locator”/”subsequent”: cites referencing previously cited items have the “subsequent” position.
|
||||
* Such cites may also have the “ibid” or “ibid-with-locator” position when:
|
||||
* a) the current cite immediately follows on another cite, within the same citation, that references the
|
||||
* same item, or
|
||||
* b) the current cite is the first cite in the citation, and the previous citation consists of a single cite
|
||||
* referencing the same item
|
||||
* If either requirement is met, the presence of locators determines which position is assigned:
|
||||
* - Preceding cite does not have a locator: if the current cite has a locator, the position of the current cite is
|
||||
* “ibid-with-locator”. Otherwise the position is “ibid”.
|
||||
* - Preceding cite does have a locator: if the current cite has the same locator, the position of the current cite
|
||||
* is “ibid”. If the locator differs the position is “ibid-with-locator”. If the current cite lacks a locator
|
||||
* its only position is “subsequent”.
|
||||
* - “near-note”: position of a cite following another cite referencing the same item. Both cites have to be located
|
||||
* in foot or endnotes, and the distance between both cites may not exceed the maximum distance (measured in number
|
||||
* of foot or endnotes) set with the near-note-distance option.
|
||||
*
|
||||
* Whenever position=”ibid-with-locator” tests true, position=”ibid” also tests true. And whenever position=”ibid” or
|
||||
* position=”near-note” test true, position=”subsequent” also tests true.
|
||||
*
|
||||
*/
|
||||
class Position implements Constraint
|
||||
{
|
||||
const FIRST = "first";
|
||||
const IBID = "ibid";
|
||||
const IBID_WITH_LOCATOR = "ibid-with-locator";
|
||||
const SUBSEQUENT = "subsequent";
|
||||
const NEAR_NOTE = "near-note";
|
||||
|
||||
private $position;
|
||||
|
||||
private $match;
|
||||
|
||||
public function __construct(string $position, string $match = "all")
|
||||
{
|
||||
$this->position = $position;
|
||||
$this->match = $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(stdClass $data, $citationNumber = null): bool
|
||||
{
|
||||
if (CiteProc::getContext()->isModeBibliography()) {
|
||||
return false;
|
||||
}
|
||||
switch ($this->position) {
|
||||
case self::FIRST:
|
||||
return $this->getPosition($data) === null;
|
||||
case self::IBID:
|
||||
case self::IBID_WITH_LOCATOR:
|
||||
case self::SUBSEQUENT:
|
||||
return $this->isOnLastPosition($data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getPosition(stdClass $data): ?string
|
||||
{
|
||||
foreach (CiteProc::getContext()->getCitedItems() as $key => $value) {
|
||||
if (!empty($value->{'id'}) && $value->{'id'} === $data->{'id'}) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @return bool
|
||||
*/
|
||||
private function isOnLastPosition(stdClass $data): bool
|
||||
{
|
||||
$lastCitedItem = CiteProc::getContext()->getCitedItems()->last();
|
||||
return !empty($lastCitedItem) && $lastCitedItem->{'id'} === $data->{'id'};
|
||||
}
|
||||
}
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class Type extends AbstractConstraint
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param stdClass $data ;
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchForVariable(string $variable, stdClass $data): bool
|
||||
{
|
||||
return in_array($data->type, $this->conditionVariables->toArray());
|
||||
}
|
||||
}
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Constraint;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use stdClass;
|
||||
|
||||
class Variable extends AbstractConstraint
|
||||
{
|
||||
/**
|
||||
* @param string $variable
|
||||
* @param stdClass $data
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchForVariable(string $variable, stdClass $data): bool
|
||||
{
|
||||
$variableExistsInCitationItem = false;
|
||||
if (CiteProc::getContext()->isModeCitation() && isset($data->id)) {
|
||||
$citationItem = CiteProc::getContext()->getCitationItemById($data->id);
|
||||
if (!empty($citationItem)) {
|
||||
$variableExistsInCitationItem = !empty($citationItem->{$variable});
|
||||
}
|
||||
}
|
||||
return !empty($data->{$variable}) || $variableExistsInCitationItem;
|
||||
}
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc;
|
||||
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Locale\Locale;
|
||||
use Seboettg\CiteProc\Root\Info;
|
||||
use Seboettg\CiteProc\Style\Bibliography;
|
||||
use Seboettg\CiteProc\Style\Citation;
|
||||
use Seboettg\CiteProc\Style\Macro;
|
||||
use Seboettg\CiteProc\Style\Options\BibliographyOptions;
|
||||
use Seboettg\CiteProc\Style\Options\CitationOptions;
|
||||
use Seboettg\CiteProc\Style\Options\GlobalOptions;
|
||||
use Seboettg\CiteProc\Style\Sort\Sort;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use Seboettg\CiteProc\Styles\Css\CssStyle;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class Context
|
||||
* @package Seboettg\CiteProc
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $macros;
|
||||
|
||||
/**
|
||||
* @var Locale
|
||||
*/
|
||||
private $locale;
|
||||
|
||||
/**
|
||||
* @var Bibliography
|
||||
*/
|
||||
private $bibliography;
|
||||
|
||||
/**
|
||||
* @var Citation
|
||||
*/
|
||||
private $citation;
|
||||
|
||||
/**
|
||||
* @var Sort
|
||||
*/
|
||||
private $sorting;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* @var DataList
|
||||
*/
|
||||
private $citationData;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $citationItems;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $results;
|
||||
|
||||
/**
|
||||
* @var Root
|
||||
*/
|
||||
private $root;
|
||||
|
||||
/**
|
||||
* @var GlobalOptions
|
||||
*/
|
||||
private $globalOptions;
|
||||
|
||||
/**
|
||||
* @var BibliographyOptions
|
||||
*/
|
||||
private $bibliographySpecificOptions;
|
||||
|
||||
/**
|
||||
* @var CitationOptions
|
||||
*/
|
||||
private $citationSpecificOptions;
|
||||
|
||||
/**
|
||||
* @var RenderingState
|
||||
*/
|
||||
private $renderingState;
|
||||
|
||||
/**
|
||||
* @var CssStyle
|
||||
*/
|
||||
private $cssStyle;
|
||||
|
||||
/**
|
||||
* @var Info
|
||||
*/
|
||||
private $info;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $markupExtension = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $citationsAsArray = false;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $citedItems;
|
||||
|
||||
public function __construct($locale = null)
|
||||
{
|
||||
if (!empty($locale)) {
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
$this->macros = new ArrayList();
|
||||
$this->citationData = new DataList();
|
||||
$this->results = new ArrayList();
|
||||
$this->renderingState = RenderingState::RENDERING();
|
||||
$this->citedItems = new ArrayList();
|
||||
}
|
||||
|
||||
public function addMacro($key, $macro)
|
||||
{
|
||||
$this->macros->add($key, $macro);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return Macro
|
||||
*/
|
||||
public function getMacro($key)
|
||||
{
|
||||
return $this->macros->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Locale $locale
|
||||
*/
|
||||
public function setLocale(Locale $locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locale
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Bibliography
|
||||
*/
|
||||
public function getBibliography()
|
||||
{
|
||||
return $this->bibliography;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bibliography $bibliography
|
||||
*/
|
||||
public function setBibliography(Bibliography $bibliography)
|
||||
{
|
||||
$this->bibliography = $bibliography;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Citation
|
||||
*/
|
||||
public function getCitation()
|
||||
{
|
||||
return $this->citation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Citation $citation
|
||||
*/
|
||||
public function setCitation($citation)
|
||||
{
|
||||
$this->citation = $citation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $citationsAsArray
|
||||
*/
|
||||
public function setCitationsAsArray($citationsAsArray = true)
|
||||
{
|
||||
$this->citationsAsArray = $citationsAsArray;
|
||||
}
|
||||
|
||||
public function isCitationsAsArray()
|
||||
{
|
||||
return $this->citationsAsArray;
|
||||
}
|
||||
|
||||
public function setSorting($sorting)
|
||||
{
|
||||
$this->sorting = $sorting;
|
||||
}
|
||||
|
||||
public function getSorting()
|
||||
{
|
||||
return $this->sorting;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the render mode (citation|bibliography)
|
||||
* @return string
|
||||
*/
|
||||
public function getMode()
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mode
|
||||
*/
|
||||
public function setMode($mode)
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the render mode is set to citation
|
||||
* @return bool
|
||||
*/
|
||||
public function isModeCitation()
|
||||
{
|
||||
return $this->mode === "citation";
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the render mode is set to bibliography
|
||||
* @return bool
|
||||
*/
|
||||
public function isModeBibliography()
|
||||
{
|
||||
return $this->mode === "bibliography";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function getCitationData()
|
||||
{
|
||||
return $this->citationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayList|DataList $citationData
|
||||
*/
|
||||
public function setCitationData($citationData)
|
||||
{
|
||||
$this->citationData = $citationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getCitationItems(): ArrayList
|
||||
{
|
||||
return $this->citationItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayList $citationItems
|
||||
*/
|
||||
public function setCitationItems(ArrayList $citationItems): void
|
||||
{
|
||||
$this->citationItems = $citationItems;
|
||||
}
|
||||
|
||||
public function hasCitationItems()
|
||||
{
|
||||
return ($this->citationData->count() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getMacros()
|
||||
{
|
||||
return $this->macros;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Root
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Root $root
|
||||
*/
|
||||
public function setRoot(Root $root)
|
||||
{
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GlobalOptions
|
||||
*/
|
||||
public function getGlobalOptions()
|
||||
{
|
||||
return $this->globalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GlobalOptions $globalOptions
|
||||
*/
|
||||
public function setGlobalOptions(GlobalOptions $globalOptions)
|
||||
{
|
||||
$this->globalOptions = $globalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RenderingState
|
||||
*/
|
||||
public function getRenderingState()
|
||||
{
|
||||
return $this->renderingState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RenderingState|string $renderingState
|
||||
*/
|
||||
public function setRenderingState(RenderingState $renderingState)
|
||||
{
|
||||
$this->renderingState = $renderingState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BibliographyOptions
|
||||
*/
|
||||
public function getBibliographySpecificOptions()
|
||||
{
|
||||
return $this->bibliographySpecificOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BibliographyOptions $bibliographySpecificOptions
|
||||
*/
|
||||
public function setBibliographySpecificOptions(BibliographyOptions $bibliographySpecificOptions)
|
||||
{
|
||||
$this->bibliographySpecificOptions = $bibliographySpecificOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CitationOptions
|
||||
*/
|
||||
public function getCitationSpecificOptions()
|
||||
{
|
||||
return $this->citationSpecificOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CitationOptions $citationSpecificOptions
|
||||
*/
|
||||
public function setCitationSpecificOptions(CitationOptions $citationSpecificOptions)
|
||||
{
|
||||
$this->citationSpecificOptions = $citationSpecificOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CssStyle $cssStyle
|
||||
*/
|
||||
public function setCssStyle(CssStyle $cssStyle)
|
||||
{
|
||||
$this->cssStyle = $cssStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CssStyle
|
||||
*/
|
||||
public function getCssStyle()
|
||||
{
|
||||
return $this->cssStyle;
|
||||
}
|
||||
|
||||
public function setInfo(Info $info)
|
||||
{
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
public function getInfo()
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMarkupExtension()
|
||||
{
|
||||
return $this->markupExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $markupExtension
|
||||
*/
|
||||
public function setMarkupExtension($markupExtension)
|
||||
{
|
||||
$this->markupExtension = $markupExtension;
|
||||
}
|
||||
|
||||
public function getCitationItemById($id)
|
||||
{
|
||||
return $this->citationItems->filter(function ($item) use ($id) {
|
||||
return $item->id === $id;
|
||||
})->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getCitedItems(): ArrayList
|
||||
{
|
||||
return $this->citedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayList $citedItems
|
||||
*/
|
||||
public function setCitedItems(ArrayList $citedItems): void
|
||||
{
|
||||
$this->citedItems = $citedItems;
|
||||
}
|
||||
|
||||
public function appendCitedItem($citedItem)
|
||||
{
|
||||
$this->citedItems->append($citedItem);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Data;
|
||||
|
||||
use Seboettg\CiteProc\Style\Citation;
|
||||
use Seboettg\CiteProc\Style\Options\SubsequentAuthorSubstituteRule;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class DataList
|
||||
*
|
||||
* @package Seboettg\CiteProc\Data
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class DataList extends ArrayList
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $subsequentAuthorSubstitute;
|
||||
|
||||
/**
|
||||
* @var SubsequentAuthorSubstituteRule
|
||||
*/
|
||||
private $subsequentAuthorSubstituteRule = "complete-all";
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSubsequentAuthorSubstitute()
|
||||
{
|
||||
return $this->subsequentAuthorSubstitute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $subsequentAuthorSubstitute
|
||||
*/
|
||||
public function setSubsequentAuthorSubstitute($subsequentAuthorSubstitute)
|
||||
{
|
||||
$this->subsequentAuthorSubstitute = $subsequentAuthorSubstitute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubsequentAuthorSubstituteRule
|
||||
*/
|
||||
public function getSubsequentAuthorSubstituteRule()
|
||||
{
|
||||
return $this->subsequentAuthorSubstituteRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SubsequentAuthorSubstituteRule $subsequentAuthorSubstituteRule
|
||||
*/
|
||||
public function setSubsequentAuthorSubstituteRule(SubsequentAuthorSubstituteRule $subsequentAuthorSubstituteRule)
|
||||
{
|
||||
$this->subsequentAuthorSubstituteRule = $subsequentAuthorSubstituteRule;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class CiteProcException
|
||||
* @package Seboettg\CiteProc\Exception
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CiteProcException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class ClassNotFoundException
|
||||
* @package Seboettg\CiteProc\Exception
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class ClassNotFoundException extends CiteProcException
|
||||
{
|
||||
|
||||
public function __construct($class, $code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct("Class \"$class\" could not be found.", $code, $previous);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: InvalidDateException.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 2019-03-01, 16:42
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Exception;
|
||||
|
||||
class InvalidDateTimeException extends CiteProcException
|
||||
{
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: InvalidStylesheetException.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 2019-03-01, 16:08
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Exception;
|
||||
|
||||
class InvalidStylesheetException extends CiteProcException
|
||||
{
|
||||
|
||||
}
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Locale;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\StyleSheet;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Locale
|
||||
*
|
||||
* While localization data can be included in styles, locale files conveniently provide sets of default localization
|
||||
* data, consisting of terms, date formats and grammar options. These default localizations are drawn from the
|
||||
* “locales-xx-XX.xml” located in locales folder (which is included as git submodule). These default locales may be
|
||||
* redefined or supplemented with cs:locale elements, which should be placed in the style sheet directly after the
|
||||
* cs:info element.
|
||||
*
|
||||
* TODO: implement Locale Fallback (http://docs.citationstyles.org/en/stable/specification.html#locale-fallback)
|
||||
*
|
||||
* @package Seboettg\CiteProc\Locale
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Locale
|
||||
{
|
||||
use LocaleXmlParserTrait;
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement
|
||||
*/
|
||||
private $localeXml;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $language;
|
||||
|
||||
/**
|
||||
* Locale constructor.
|
||||
* @param string $lang
|
||||
* @param ?string $xmlString
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function __construct($lang = "en-US", $xmlString = null)
|
||||
{
|
||||
$this->language = $lang;
|
||||
|
||||
if (!empty($xmlString)) {
|
||||
$this->localeXml = new SimpleXMLElement($xmlString);
|
||||
} else {
|
||||
$this->localeXml = new SimpleXMLElement(StyleSheet::loadLocales($lang));
|
||||
}
|
||||
|
||||
$this->initLocaleXmlParser();
|
||||
$this->parseXml($this->localeXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $xml
|
||||
* @return $this
|
||||
*/
|
||||
public function addXml(SimpleXMLElement $xml)
|
||||
{
|
||||
$lang = (string) $xml->attributes('http://www.w3.org/XML/1998/namespace')->{'lang'};
|
||||
if (empty($lang) || $this->getLanguage() === $lang || explode('-', $this->getLanguage())[0] === $lang) {
|
||||
$this->parseXml($xml);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param $name
|
||||
* @param string $form
|
||||
* @return stdClass
|
||||
*/
|
||||
public function filter($type, $name, $form = "long")
|
||||
{
|
||||
if ('options' === $type) {
|
||||
return $this->option($name);
|
||||
}
|
||||
if (!isset($this->{$type})) {
|
||||
throw new InvalidArgumentException("There is no locale of type \"$type\".");
|
||||
}
|
||||
|
||||
/** @var ArrayList $localeList */
|
||||
$localeList = $this->{$type};
|
||||
|
||||
if (is_null($name)) {
|
||||
$name = "";
|
||||
}
|
||||
|
||||
//filter by name
|
||||
$array = $localeList->get($name);
|
||||
|
||||
if (empty($array)) {
|
||||
$ret = new stdClass();
|
||||
$ret->name = null;
|
||||
$ret->single = null;
|
||||
$ret->multiple = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
//filter by form
|
||||
if ($type !== "options") {
|
||||
/** @var Term $value */
|
||||
$array = array_filter($array, function ($term) use ($form) {
|
||||
return $term->form === $form;
|
||||
});
|
||||
}
|
||||
|
||||
return array_pop($array);
|
||||
}
|
||||
|
||||
private function option($name)
|
||||
{
|
||||
$result = null;
|
||||
foreach ($this->options as $key => $value) {
|
||||
if ($key === $name) {
|
||||
if (is_array($value) && isset($value[1]) && is_array($value[1])) {
|
||||
$result = reset($value[1]);
|
||||
} else {
|
||||
$result = reset($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Locale;
|
||||
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Trait LocaleXmlParserTrait
|
||||
* @package Seboettg\CiteProc\Locale
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait LocaleXmlParserTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $terms;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $optionsXml;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $dateXml;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $termsXml;
|
||||
|
||||
/**
|
||||
* init parser
|
||||
*/
|
||||
protected function initLocaleXmlParser()
|
||||
{
|
||||
$this->options = new ArrayList();
|
||||
$this->optionsXml = new ArrayList();
|
||||
$this->date = new ArrayList();
|
||||
$this->dateXml = new ArrayList();
|
||||
$this->terms = new ArrayList();
|
||||
$this->termsXml = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $locale
|
||||
*/
|
||||
private function parseXml(SimpleXMLElement $locale)
|
||||
{
|
||||
/** @var SimpleXMLElement $node */
|
||||
foreach ($locale as $node) {
|
||||
switch ($node->getName()) {
|
||||
case 'style-options':
|
||||
$this->optionsXml->add('options', $node);
|
||||
foreach ($node->attributes() as $name => $value) {
|
||||
if ((string) $value == 'true') {
|
||||
$this->options->add($name, [true]);
|
||||
} else {
|
||||
$this->options->add($name, [false]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'terms':
|
||||
$this->termsXml->add('terms', $node);
|
||||
$plural = ['single', 'multiple'];
|
||||
|
||||
/** @var SimpleXMLElement $child */
|
||||
foreach ($node->children() as $child) {
|
||||
$term = new Term();
|
||||
|
||||
foreach ($child->attributes() as $key => $value) {
|
||||
$term->{$key} = (string) $value;
|
||||
}
|
||||
|
||||
$subChildren = $child->children();
|
||||
$count = $subChildren->count();
|
||||
if ($count > 0) {
|
||||
/** @var SimpleXMLElement $subChild */
|
||||
foreach ($subChildren as $subChild) {
|
||||
$name = $subChild->getName();
|
||||
$value = (string) $subChild;
|
||||
if (in_array($subChild->getName(), $plural)) {
|
||||
$term->{$name} = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$value = (string) $child;
|
||||
$term->{'single'} = $value;
|
||||
$term->{'multiple'} = $value;
|
||||
}
|
||||
if (!$this->terms->hasKey($term->getName())) {
|
||||
$this->terms->add($term->getName(), []);
|
||||
}
|
||||
|
||||
$this->terms->add($term->getName(), $term);
|
||||
}
|
||||
break;
|
||||
case 'date':
|
||||
$form = (string) $node["form"];
|
||||
$this->dateXml->add($form, $node);
|
||||
foreach ($node->children() as $child) {
|
||||
$date = new stdClass();
|
||||
$name = "";
|
||||
foreach ($child->attributes() as $key => $value) {
|
||||
if ("name" === $key) {
|
||||
$name = (string) $value;
|
||||
}
|
||||
$date->{$key} = (string) $value;
|
||||
}
|
||||
if ($child->getName() !== "name-part" && !$this->terms->hasKey($name)) {
|
||||
$this->terms->add($name, []);
|
||||
}
|
||||
$this->date->add($form, $date);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SimpleXMLElement
|
||||
*/
|
||||
public function getLatestOptionsXml()
|
||||
{
|
||||
$arr = $this->optionsXml->toArray();
|
||||
return array_pop($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDateXml()
|
||||
{
|
||||
return $this->dateXml->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SimpleXMLElement
|
||||
*/
|
||||
public function getLatestDateXml()
|
||||
{
|
||||
$arr = $this->dateXml->toArray();
|
||||
return array_pop($arr['date']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SimpleXMLElement
|
||||
*/
|
||||
public function getTermsXml()
|
||||
{
|
||||
$arr = $this->termsXml->toArray();
|
||||
return array_pop($arr);
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php /** @noinspection PhpUnusedPrivateFieldInspection */
|
||||
|
||||
/**
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Locale;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function Seboettg\CiteProc\ucfirst;
|
||||
|
||||
/**
|
||||
* Class Term
|
||||
* @package Seboettg\CiteProc\Locale
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Term
|
||||
{
|
||||
|
||||
private $name = "";
|
||||
|
||||
private $form = "long";
|
||||
|
||||
private $single = "";
|
||||
|
||||
private $multiple = "";
|
||||
|
||||
private $match = "";
|
||||
|
||||
private $genderForm = "";
|
||||
|
||||
private $gender = "";
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$nameParts = explode("-", $name);
|
||||
$attr = "";
|
||||
for ($i = count($nameParts) - 1; $i >= 0; --$i) {
|
||||
if ($i > 0) {
|
||||
$attr = ucfirst($nameParts[$i]) . $attr;
|
||||
} else {
|
||||
$attr = $nameParts[$i] . $attr;
|
||||
}
|
||||
}
|
||||
if (!isset($this->{$attr})) {
|
||||
throw new InvalidArgumentException("Property \"$attr\" ($name) does not exist in " . __CLASS__);
|
||||
}
|
||||
$this->{$attr} = $value;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->{$name};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Choose;
|
||||
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\ClassNotFoundException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Choose
|
||||
*
|
||||
* @package Seboettg\CiteProc\Node
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Choose implements Rendering, HasParent
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $children;
|
||||
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* Choose constructor.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
* @param $parent
|
||||
* @throws ClassNotFoundException
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->children = new ArrayList();
|
||||
$elseIf = new ArrayList();
|
||||
foreach ($node->children() as $child) {
|
||||
switch ($child->getName()) {
|
||||
case 'if':
|
||||
$this->children->add("if", new ChooseIf($child, $this));
|
||||
break;
|
||||
case 'else-if':
|
||||
$elseIf->append(new ChooseElseIf($child, $this));
|
||||
break;
|
||||
case 'else':
|
||||
$this->children->add("else", new ChooseElse($child, $this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($elseIf->count() > 0) {
|
||||
$this->children->add("elseif", $elseIf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param null|int $citationNumber
|
||||
* @return string
|
||||
* @throws ArrayList\NotConvertibleToStringException
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$result = new ArrayList();
|
||||
$matchedIfs = false;
|
||||
|
||||
$ifCondition = $this->children->get("if");
|
||||
|
||||
if ($ifCondition->match($data)) { //IF CONDITION
|
||||
$matchedIfs = true;
|
||||
$result->append($ifCondition->render($data));
|
||||
} elseif ($this->children->hasKey("elseif")) { // ELSEIF
|
||||
$elseIfs = $this->children->get("elseif")
|
||||
->map(function (ChooseIf $elseIf) use ($data) {
|
||||
return new Tuple($elseIf, $elseIf->match($data));
|
||||
})
|
||||
->filter(function (Tuple $elseIfToMatch) {
|
||||
return $elseIfToMatch->second === true;
|
||||
});
|
||||
$matchedIfs = $elseIfs->count() > 0;
|
||||
if ($matchedIfs) {
|
||||
$result->append(
|
||||
$elseIfs
|
||||
->first() //returns a Tuple
|
||||
->first
|
||||
->render($data)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// !$matchedIfs ensures that each previous condition has not been met
|
||||
if (!$matchedIfs && $this->children->hasKey("else")) { //ELSE
|
||||
$result->append($this->children->get("else")->render($data));
|
||||
}
|
||||
return $result->collectToString("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Choose;
|
||||
|
||||
class ChooseElse extends ChooseIf
|
||||
{
|
||||
//render function is inherited from ChooseIf
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Choose;
|
||||
|
||||
class ChooseElseIf extends ChooseElse
|
||||
{
|
||||
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Choose;
|
||||
|
||||
use Seboettg\CiteProc\Constraint\Constraint;
|
||||
use Seboettg\CiteProc\Constraint\Factory;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\ClassNotFoundException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\Group;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class ChooseIf implements Rendering, HasParent
|
||||
{
|
||||
/**
|
||||
* @var ArrayList<Constraint>|Constraint[]
|
||||
*/
|
||||
private $constraints;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $match;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $parent;
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Choose $parent
|
||||
* @throws InvalidStylesheetException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, Choose $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->constraints = new ArrayList();
|
||||
$this->children = new ArrayList();
|
||||
$this->match = (string) $node['match'];
|
||||
if (empty($this->match)) {
|
||||
$this->match = Constraint::MATCH_ALL;
|
||||
}
|
||||
foreach ($node->attributes() as $name => $value) {
|
||||
if ('match' !== $name) {
|
||||
$this->constraints->append(Factory::createConstraint((string) $name, (string) $value, $this->match));
|
||||
}
|
||||
}
|
||||
foreach ($node->children() as $child) {
|
||||
$this->children->append(Factory::create($child, $this));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param null|int $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null): string
|
||||
{
|
||||
$ret = [];
|
||||
/** @var Rendering $child */
|
||||
foreach ($this->children as $child) {
|
||||
$ret[] = $child->render($data, $citationNumber);
|
||||
}
|
||||
$glue = "";
|
||||
$parent = $this->parent->getParent();
|
||||
if ($parent instanceof Group && $parent->hasDelimiter()) {
|
||||
$glue = $parent->getDelimiter();
|
||||
}
|
||||
return implode($glue, array_filter($ret));
|
||||
}
|
||||
/**
|
||||
* @param $data
|
||||
* @param null|int $citationNumber
|
||||
* @return bool
|
||||
*/
|
||||
public function match($data, int $citationNumber = null): bool
|
||||
{
|
||||
if ($this->constraints->count() === 1) {
|
||||
return $this->constraints->current()->validate($data);
|
||||
}
|
||||
|
||||
switch ($this->match) {
|
||||
case Constraint::MATCH_ANY:
|
||||
return $this->constraints
|
||||
->map(function (Constraint $constraint) use ($data) {
|
||||
return $constraint->validate($data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === true;
|
||||
})
|
||||
->count() > 0;
|
||||
case Constraint::MATCH_ALL:
|
||||
return $this->constraints
|
||||
->map(function (Constraint $constraint) use ($data) {
|
||||
return $constraint->validate($data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === true;
|
||||
})
|
||||
->count() === $this->constraints->count();
|
||||
case Constraint::MATCH_NONE:
|
||||
return !$this->constraints
|
||||
->map(function (Constraint $constraint) use ($data) {
|
||||
return $constraint->validate($data);
|
||||
})
|
||||
->filter(function (bool $match) {
|
||||
return $match === false;
|
||||
})
|
||||
->count() === $this->constraints->count();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
* @return Choose
|
||||
*/
|
||||
public function getParent(): Choose
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2022 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Choose;
|
||||
|
||||
class Tuple
|
||||
{
|
||||
public $first;
|
||||
public $second;
|
||||
|
||||
public function __construct($first, $second)
|
||||
{
|
||||
$this->first = $first;
|
||||
$this->second = $second;
|
||||
}
|
||||
}
|
||||
Vendored
+412
@@ -0,0 +1,412 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date;
|
||||
|
||||
use Exception;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateRange\DateRangeRenderer;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\DisplayTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use Seboettg\CiteProc\Util;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Date
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Date
|
||||
{
|
||||
use AffixesTrait,
|
||||
DisplayTrait,
|
||||
FormattingTrait,
|
||||
TextCaseTrait;
|
||||
|
||||
// bitmask: ymd
|
||||
const DATE_RANGE_STATE_NONE = 0; // 000
|
||||
const DATE_RANGE_STATE_DAY = 1; // 001
|
||||
const DATE_RANGE_STATE_MONTH = 2; // 010
|
||||
const DATE_RANGE_STATE_MONTHDAY = 3; // 011
|
||||
const DATE_RANGE_STATE_YEAR = 4; // 100
|
||||
const DATE_RANGE_STATE_YEARDAY = 5; // 101
|
||||
const DATE_RANGE_STATE_YEARMONTH = 6; // 110
|
||||
const DATE_RANGE_STATE_YEARMONTHDAY = 7; // 111
|
||||
|
||||
private static $localizedDateFormats = [
|
||||
'numeric',
|
||||
'text'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $dateParts;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $form = "";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $variable = "";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $datePartsAttribute = "";
|
||||
|
||||
/**
|
||||
* Date constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
$this->dateParts = new ArrayList();
|
||||
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'form':
|
||||
$this->form = (string) $attribute;
|
||||
break;
|
||||
case 'variable':
|
||||
$this->variable = (string) $attribute;
|
||||
break;
|
||||
case 'date-parts':
|
||||
$this->datePartsAttribute = (string) $attribute;
|
||||
}
|
||||
}
|
||||
/** @var SimpleXMLElement $child */
|
||||
foreach ($node->children() as $child) {
|
||||
if ($child->getName() === "date-part") {
|
||||
$datePartName = (string) $child->attributes()["name"];
|
||||
$this->dateParts->set($this->form . "-" . $datePartName, Util\Factory::create($child));
|
||||
}
|
||||
}
|
||||
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initDisplayAttributes($node);
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
* @throws InvalidStylesheetException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function render($data)
|
||||
{
|
||||
$ret = "";
|
||||
$var = null;
|
||||
if (isset($data->{$this->variable})) {
|
||||
$var = $data->{$this->variable};
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
$this->prepareDatePartsInVariable($data, $var);
|
||||
} catch (CiteProcException $e) {
|
||||
if (isset($data->{$this->variable}->{'raw'}) &&
|
||||
!preg_match("/(\p{L}+)\s?([\-\–&,])\s?(\p{L}+)/u", $data->{$this->variable}->{'raw'})) {
|
||||
return $this->addAffixes($this->format($this->applyTextCase($data->{$this->variable}->{'raw'})));
|
||||
} else {
|
||||
if (isset($data->{$this->variable}->{'string-literal'})) {
|
||||
return $this->addAffixes(
|
||||
$this->format($this->applyTextCase($data->{$this->variable}->{'string-literal'}))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form = $this->form;
|
||||
$dateParts = !empty($this->datePartsAttribute) ? explode("-", $this->datePartsAttribute) : [];
|
||||
$this->prepareDatePartsChildren($dateParts, $form);
|
||||
|
||||
// No date-parts in date-part attribute defined, take into account that the defined date-part children will
|
||||
// be used.
|
||||
if (empty($this->datePartsAttribute) && $this->dateParts->count() > 0) {
|
||||
/** @var DatePart $part */
|
||||
foreach ($this->dateParts as $part) {
|
||||
$dateParts[] = $part->getName();
|
||||
}
|
||||
}
|
||||
|
||||
/* cs:date may have one or more cs:date-part child elements (see Date-part). The attributes set on
|
||||
these elements override those specified for the localized date formats (e.g. to get abbreviated months for all
|
||||
locales, the form attribute on the month-cs:date-part element can be set to “short”). These cs:date-part
|
||||
elements do not affect which, or in what order, date parts are rendered. Affixes, which are very
|
||||
locale-specific, are not allowed on these cs:date-part elements. */
|
||||
|
||||
if ($this->dateParts->count() > 0) {
|
||||
if (!isset($var->{'date-parts'})) { // ignore empty date-parts
|
||||
return "";
|
||||
}
|
||||
|
||||
if (count($data->{$this->variable}->{'date-parts'}) === 1) {
|
||||
$data_ = $this->createDateTime($data->{$this->variable}->{'date-parts'});
|
||||
$ret .= $this->iterateAndRenderDateParts($dateParts, $data_);
|
||||
} elseif (count($var->{'date-parts'}) === 2) { //date range
|
||||
$data_ = $this->createDateTime($var->{'date-parts'});
|
||||
$from = $data_[0];
|
||||
$to = $data_[1];
|
||||
$interval = $to->diff($from);
|
||||
$delimiter = "";
|
||||
$toRender = 0;
|
||||
if ($interval->y > 0 && in_array('year', $dateParts)) {
|
||||
$toRender |= self::DATE_RANGE_STATE_YEAR;
|
||||
$delimiter = $this->dateParts->get($this->form."-year")->getRangeDelimiter();
|
||||
}
|
||||
if ($interval->m > 0 && $from->getMonth() - $to->getMonth() !== 0 && in_array('month', $dateParts)) {
|
||||
$toRender |= self::DATE_RANGE_STATE_MONTH;
|
||||
$delimiter = $this->dateParts->get($this->form."-month")->getRangeDelimiter();
|
||||
}
|
||||
if ($interval->d > 0 && $from->getDay() - $to->getDay() !== 0 && in_array('day', $dateParts)) {
|
||||
$toRender |= self::DATE_RANGE_STATE_DAY;
|
||||
$delimiter = $this->dateParts->get($this->form."-day")->getRangeDelimiter();
|
||||
}
|
||||
if ($toRender === self::DATE_RANGE_STATE_NONE) {
|
||||
$ret .= $this->iterateAndRenderDateParts($dateParts, $data_);
|
||||
} else {
|
||||
$ret .= $this->renderDateRange($toRender, $from, $to, $delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($var->raw) && preg_match("/(\p{L}+)\s?([\-\–&,])\s?(\p{L}+)/u", $var->raw, $matches)) {
|
||||
return $matches[1].$matches[2].$matches[3];
|
||||
}
|
||||
} elseif (!empty($this->datePartsAttribute)) {
|
||||
// fallback:
|
||||
// When there are no dateParts children, but date-parts attribute in date
|
||||
// render numeric
|
||||
$data = $this->createDateTime($var->{'date-parts'});
|
||||
$ret = $this->renderNumeric($data[0]);
|
||||
}
|
||||
|
||||
return !empty($ret) ? $this->addAffixes($this->format($this->applyTextCase($ret))) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dates
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function createDateTime($dates)
|
||||
{
|
||||
$data = [];
|
||||
foreach ($dates as $date) {
|
||||
$date = $this->cleanDate($date);
|
||||
if ($date[0] < 1000) {
|
||||
$dateTime = new DateTime(0, 0, 0);
|
||||
$dateTime->setDay(0)->setMonth(0)->setYear(0);
|
||||
$data[] = $dateTime;
|
||||
}
|
||||
$dateTime = new DateTime(
|
||||
$date[0],
|
||||
array_key_exists(1, $date) ? $date[1] : 1,
|
||||
array_key_exists(2, $date) ? $date[2] : 1
|
||||
);
|
||||
if (!array_key_exists(1, $date)) {
|
||||
$dateTime->setMonth(0);
|
||||
}
|
||||
if (!array_key_exists(2, $date)) {
|
||||
$dateTime->setDay(0);
|
||||
}
|
||||
$data[] = $dateTime;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $toRender
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
private function renderDateRange($toRender, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$datePartRenderer = DateRangeRenderer::factory($this, $toRender);
|
||||
return $datePartRenderer->parseDateRange($this->dateParts, $from, $to, $delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $format
|
||||
* @return bool
|
||||
*/
|
||||
private function hasDatePartsFromLocales($format)
|
||||
{
|
||||
$dateXml = CiteProc::getContext()->getLocale()->getDateXml();
|
||||
return !empty($dateXml[$format]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $format
|
||||
* @return array
|
||||
*/
|
||||
private function getDatePartsFromLocales($format)
|
||||
{
|
||||
$ret = [];
|
||||
// date parts from locales
|
||||
$dateFromLocale_ = CiteProc::getContext()->getLocale()->getDateXml();
|
||||
$dateFromLocale = $dateFromLocale_[$format];
|
||||
|
||||
// no custom date parts within the date element (this)?
|
||||
if (!empty($dateFromLocale)) {
|
||||
$dateForm = array_filter(
|
||||
is_array($dateFromLocale) ? $dateFromLocale : [$dateFromLocale],
|
||||
function ($element) use ($format) {
|
||||
/** @var SimpleXMLElement $element */
|
||||
$dateForm = (string) $element->attributes()["form"];
|
||||
return $dateForm === $format;
|
||||
}
|
||||
);
|
||||
|
||||
//has dateForm from locale children (date-part elements)?
|
||||
$localeDate = array_pop($dateForm);
|
||||
|
||||
if ($localeDate instanceof SimpleXMLElement && $localeDate->count() > 0) {
|
||||
foreach ($localeDate as $child) {
|
||||
$ret[] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVariable()
|
||||
{
|
||||
return $this->variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $var
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function prepareDatePartsInVariable($data, $var)
|
||||
{
|
||||
if (!isset($data->{$this->variable}->{'date-parts'}) || empty($data->{$this->variable}->{'date-parts'})) {
|
||||
if (isset($data->{$this->variable}->raw) && !empty($data->{$this->variable}->raw)) {
|
||||
// try to parse date parts from "raw" attribute
|
||||
$var->{'date-parts'} = Util\DateHelper::parseDateParts($data->{$this->variable});
|
||||
} else {
|
||||
throw new CiteProcException("No valid date format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $dateParts
|
||||
* @param string $form
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
private function prepareDatePartsChildren($dateParts, $form)
|
||||
{
|
||||
/* Localized date formats are selected with the optional form attribute, which must set to either “numeric”
|
||||
(for fully numeric formats, e.g. “12-15-2005”), or “text” (for formats with a non-numeric month, e.g.
|
||||
“December 15, 2005”). Localized date formats can be customized in two ways. First, the date-parts attribute may
|
||||
be used to show fewer date parts. The possible values are:
|
||||
- “year-month-day” - (default), renders the year, month and day
|
||||
- “year-month” - renders the year and month
|
||||
- “year” - renders the year */
|
||||
|
||||
if ($this->dateParts->count() < 1 && in_array($form, self::$localizedDateFormats)) {
|
||||
if ($this->hasDatePartsFromLocales($form)) {
|
||||
$datePartsFromLocales = $this->getDatePartsFromLocales($form);
|
||||
array_filter($datePartsFromLocales, function (SimpleXMLElement $item) use ($dateParts) {
|
||||
return in_array($item["name"], $dateParts);
|
||||
});
|
||||
|
||||
foreach ($datePartsFromLocales as $datePartNode) {
|
||||
$datePart = $datePartNode["name"];
|
||||
$this->dateParts->set("$form-$datePart", Util\Factory::create($datePartNode));
|
||||
}
|
||||
} else { //otherwise create default date parts
|
||||
foreach ($dateParts as $datePart) {
|
||||
$this->dateParts->add(
|
||||
"$form-$datePart",
|
||||
new DatePart(
|
||||
new SimpleXMLElement('<date-part name="'.$datePart.'" form="'.$form.'" />')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function renderNumeric(DateTime $date)
|
||||
{
|
||||
return $date->renderNumeric();
|
||||
}
|
||||
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
private function cleanDate($date)
|
||||
{
|
||||
$ret = [];
|
||||
foreach ($date as $key => $datePart) {
|
||||
$ret[$key] = Util\NumberHelper::extractNumber(Util\StringHelper::removeBrackets($datePart));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dateParts
|
||||
* @param array $data_
|
||||
* @return string
|
||||
*/
|
||||
private function iterateAndRenderDateParts(array $dateParts, array $data_)
|
||||
{
|
||||
$result = [];
|
||||
/** @var DatePart $datePart */
|
||||
foreach ($this->dateParts as $key => $datePart) {
|
||||
/** @noinspection PhpUnusedLocalVariableInspection */
|
||||
list($f, $p) = explode("-", $key);
|
||||
if (in_array($p, $dateParts)) {
|
||||
$result[] = $datePart->render($data_[0], $this);
|
||||
}
|
||||
}
|
||||
$result = array_filter($result);
|
||||
$glue = $this->datePartsHaveAffixes() ? "" : " ";
|
||||
$return = implode($glue, $result);
|
||||
return trim($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function datePartsHaveAffixes()
|
||||
{
|
||||
$result = $this->dateParts->filter(function (DatePart $datePart) {
|
||||
return $datePart->renderSuffix() !== "" || $datePart->renderPrefix() !== "";
|
||||
});
|
||||
return $result->count() > 0;
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
/**
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Rendering\Layout;
|
||||
use Seboettg\CiteProc\Rendering\Number;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\RangeDelimiterTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class DatePart
|
||||
* @package Seboettg\CiteProc\Rendering\Date
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class DatePart
|
||||
{
|
||||
|
||||
const DEFAULT_RANGE_DELIMITER = "–";
|
||||
|
||||
use FormattingTrait,
|
||||
AffixesTrait,
|
||||
TextCaseTrait,
|
||||
RangeDelimiterTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rangeDelimiter;
|
||||
|
||||
/**
|
||||
* @var Date
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
if ("name" === $attribute->getName()) {
|
||||
$this->name = (string) $attribute;
|
||||
}
|
||||
if ("form" === $attribute->getName()) {
|
||||
$this->form = (string) $attribute;
|
||||
}
|
||||
if ("range-delimiter" === $attribute->getName()) {
|
||||
$this->rangeDelimiter = (string) $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->rangeDelimiter)) {
|
||||
$this->rangeDelimiter = self::DEFAULT_RANGE_DELIMITER;
|
||||
}
|
||||
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @param Date $parent
|
||||
* @return string
|
||||
*/
|
||||
public function render(DateTime $date, Date $parent)
|
||||
{
|
||||
$this->parent = $parent; //set parent
|
||||
$text = $this->renderWithoutAffixes($date);
|
||||
return !empty($text) ? $this->addAffixes($text) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @param Date|null $parent
|
||||
* @return string
|
||||
*/
|
||||
public function renderWithoutAffixes(DateTime $date, Date $parent = null)
|
||||
{
|
||||
if (!is_null($parent)) {
|
||||
$this->parent = $parent;
|
||||
}
|
||||
$text = "";
|
||||
switch ($this->name) {
|
||||
case 'year':
|
||||
$text = $this->renderYear($date);
|
||||
break;
|
||||
case 'month':
|
||||
$text = $this->renderMonth($date);
|
||||
break;
|
||||
case 'day':
|
||||
$text = $this->renderDay($date);
|
||||
}
|
||||
|
||||
return !empty($text) ? $this->format($this->applyTextCase($text)) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRangeDelimiter()
|
||||
{
|
||||
return $this->rangeDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @return string|int
|
||||
*/
|
||||
protected function renderYear(DateTime $date)
|
||||
{
|
||||
$text = $date->getYear();
|
||||
if ($text > 0 && $text < 1000) {
|
||||
$text = $text . CiteProc::getContext()->getLocale()->filter("terms", "ad")->single;
|
||||
return $text;
|
||||
} elseif ($text < 0) {
|
||||
$text = $text * -1;
|
||||
$text = $text . CiteProc::getContext()->getLocale()->filter("terms", "bc")->single;
|
||||
return $text;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @return string
|
||||
*/
|
||||
protected function renderMonth(DateTime $date)
|
||||
{
|
||||
if ($date->getMonth() < 1 || $date->getMonth() > 12) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$text = $date->getMonth();
|
||||
|
||||
$form = !empty($this->form) ? $this->form : "long";
|
||||
switch ($form) {
|
||||
case 'numeric':
|
||||
break;
|
||||
case 'numeric-leading-zeros':
|
||||
$text = sprintf("%02d", $text);
|
||||
break;
|
||||
case 'short':
|
||||
case 'long':
|
||||
default:
|
||||
$text = $this->monthFromLocale($text, $form);
|
||||
break;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @return int|string
|
||||
*/
|
||||
protected function renderDay(DateTime $date)
|
||||
{
|
||||
if ($date->getDay() < 1 || $date->getDay() > 31) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$text = $date->getDay();
|
||||
$form = !empty($this->form) ? $this->form : $this->parent->getForm();
|
||||
switch ($form) {
|
||||
case 'numeric':
|
||||
break;
|
||||
case 'numeric-leading-zeros':
|
||||
$text = sprintf("%02d", $text);
|
||||
break;
|
||||
case 'ordinal':
|
||||
$limitDayOrdinals =
|
||||
CiteProc::getContext()->getLocale()->filter("options", "limit-day-ordinals-to-day-1");
|
||||
if (!$limitDayOrdinals || Layout::getNumberOfCitedItems() <= 1) {
|
||||
$text = Number::ordinal($text);
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @param $form
|
||||
* @return mixed
|
||||
*/
|
||||
protected function monthFromLocale($text, $form)
|
||||
{
|
||||
if (empty($form)) {
|
||||
$form = "long";
|
||||
}
|
||||
$month = 'month-' . sprintf('%02d', $text);
|
||||
$text = CiteProc::getContext()->getLocale()->filter('terms', $month, $form)->single;
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeParser.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:00
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\CiteProc\Rendering\Date\Date;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class DatePartRenderer
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
abstract class DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Date
|
||||
*/
|
||||
protected $parentDateObject;
|
||||
|
||||
/**
|
||||
* @param Date $dateObject
|
||||
* @param int $toRender
|
||||
* @return DateRangeRenderer
|
||||
*/
|
||||
public static function factory(Date $dateObject, $toRender)
|
||||
{
|
||||
$className = self::getRenderer($toRender);
|
||||
return new $className($dateObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* DatePartRenderer constructor.
|
||||
*
|
||||
* @param Date $parentDateObject
|
||||
*/
|
||||
public function __construct(Date $parentDateObject)
|
||||
{
|
||||
$this->parentDateObject = $parentDateObject;
|
||||
}
|
||||
|
||||
private static function getRenderer($toRender)
|
||||
{
|
||||
$className = "";
|
||||
switch ($toRender) {
|
||||
case Date::DATE_RANGE_STATE_DAY:
|
||||
$className = "DayRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_MONTH:
|
||||
$className = "MonthRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_YEAR:
|
||||
$className = "YearRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_MONTHDAY:
|
||||
$className = "MonthDayRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_YEARDAY:
|
||||
$className = "YearDayRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_YEARMONTH:
|
||||
$className = "YearMonthRenderer";
|
||||
break;
|
||||
case Date::DATE_RANGE_STATE_YEARMONTHDAY:
|
||||
$className = "YearMonthDayRenderer";
|
||||
break;
|
||||
}
|
||||
return __NAMESPACE__ . "\\" . $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
abstract public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter);
|
||||
|
||||
/**
|
||||
* @param DatePart $datePart
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
protected function renderOneRangePart(DatePart $datePart, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$prefix = $datePart->renderPrefix();
|
||||
$from = $datePart->renderWithoutAffixes($from, $this->parentDateObject);
|
||||
$to = $datePart->renderWithoutAffixes($to, $this->parentDateObject);
|
||||
$suffix = !empty($to) ? $datePart->renderSuffix() : "";
|
||||
return $prefix . $from . $delimiter . $to . $suffix;
|
||||
}
|
||||
|
||||
protected function renderDateParts($dateParts, $from, $to, $delimiter)
|
||||
{
|
||||
$ret = "";
|
||||
foreach ($dateParts as $datePart) {
|
||||
if (is_array($datePart)) {
|
||||
$renderedFrom = $datePart[0]->render($from, $this->parentDateObject);
|
||||
$renderedFrom .= $datePart[1]->renderPrefix();
|
||||
$renderedFrom .= $datePart[1]->renderWithoutAffixes($from, $this->parentDateObject);
|
||||
$renderedTo = $datePart[0]->renderWithoutAffixes($to, $this->parentDateObject);
|
||||
$renderedTo .= $datePart[0]->renderSuffix();
|
||||
$renderedTo .= $datePart[1]->render($to, $this->parentDateObject);
|
||||
$ret .= $renderedFrom . $delimiter . $renderedTo;
|
||||
} else {
|
||||
$ret .= $datePart->render($from, $this->parentDateObject);
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeDayRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:09
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class DayRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
class DayRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$ret = "";
|
||||
foreach ($dateParts as $key => $datePart) {
|
||||
if (strpos($key, "year") !== false) {
|
||||
$ret .= $datePart->render($from, $this->parentDateObject);
|
||||
}
|
||||
if (strpos($key, "month") !== false) {
|
||||
$ret .= $datePart->render($from, $this->parentDateObject);
|
||||
}
|
||||
if (strpos($key, "day")) {
|
||||
$ret .= $this->renderOneRangePart($datePart, $from, $to, $delimiter);
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeMonthDayRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:51
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class MonthDayRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
class MonthDayRenderer extends DateRangeRenderer
|
||||
{
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$dp = $dateParts->toArray();
|
||||
$dateParts_ = [];
|
||||
array_walk($dp, function ($datePart, $key) use (&$dateParts_) {
|
||||
//$bit = sprintf("%03d", decbin($differentParts));
|
||||
if (strpos($key, "month") !== false || strpos($key, "day") !== false) {
|
||||
$dateParts_["monthday"][] = $datePart;
|
||||
}
|
||||
if (strpos($key, "year") !== false) {
|
||||
$dateParts_["year"] = $datePart;
|
||||
}
|
||||
});
|
||||
return $this->renderDateParts($dateParts_, $from, $to, $delimiter);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeMonthRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:09
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class DateRangeMonthRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date
|
||||
*/
|
||||
class MonthRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$ret = "";
|
||||
|
||||
foreach ($dateParts as $key => $datePart) {
|
||||
if (strpos($key, "year") !== false) {
|
||||
$ret .= $datePart->render($from, $this->parentDateObject);
|
||||
}
|
||||
if (strpos($key, "month")) {
|
||||
$ret .= $this->renderOneRangePart($datePart, $from, $to, $delimiter);
|
||||
}
|
||||
if (strpos($key, "day") !== false) {
|
||||
$day = !empty($d = $from->getDay()) ? $datePart->render($from, $this->parentDateObject) : "";
|
||||
$ret .= $day;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeYearDayRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:47
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class YearDayRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
class YearDayRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$dp = $dateParts->toArray();
|
||||
$dateParts_ = [];
|
||||
array_walk($dp, function ($datePart, $key) use (&$dateParts_) {
|
||||
if (strpos($key, "year") !== false || strpos($key, "day") !== false) {
|
||||
$dateParts_["yearday"][] = $datePart;
|
||||
}
|
||||
if (strpos($key, "month") !== false) {
|
||||
$dateParts_["month"] = $datePart;
|
||||
}
|
||||
});
|
||||
return $this->renderDateParts($dateParts_, $from, $to, $delimiter);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeYearMonthDayRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:24
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class YearMonthDayRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
class YearMonthDayRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$ret = "";
|
||||
$i = 0;
|
||||
foreach ($dateParts as $datePart) {
|
||||
if ($i === $dateParts->count() - 1) {
|
||||
$ret .= $datePart->renderPrefix();
|
||||
$ret .= $datePart->renderWithoutAffixes($from, $this->parentDateObject);
|
||||
} else {
|
||||
$ret .= $datePart->render($from, $this->parentDateObject);
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$ret .= $delimiter;
|
||||
$i = 0;
|
||||
/** @var DatePart $datePart */
|
||||
foreach ($dateParts as $datePart) {
|
||||
if ($i == 0) {
|
||||
$ret .= $datePart->renderWithoutAffixes($to, $this->parentDateObject);
|
||||
$ret .= $datePart->renderSuffix();
|
||||
} else {
|
||||
$ret .= $datePart->render($to, $this->parentDateObject);
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeYearMonthRenderer.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:36
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class YearMonthRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date\DateRange
|
||||
*/
|
||||
class YearMonthRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$dp = $dateParts->toArray();
|
||||
$dateParts_ = [];
|
||||
array_walk($dp, function ($datePart, $key) use (&$dateParts_) {
|
||||
if (strpos($key, "year") !== false || strpos($key, "month") !== false) {
|
||||
$dateParts_["yearmonth"][] = $datePart;
|
||||
}
|
||||
if (strpos($key, "day") !== false) {
|
||||
$dateParts_["day"] = $datePart;
|
||||
}
|
||||
});
|
||||
return $this->renderDateParts($dateParts_, $from, $to, $delimiter);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: DateRangeYearParser.php
|
||||
* User: Sebastian Böttger <sebastian.boettger@thomascook.de>
|
||||
* created at 03.11.19, 20:01
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date\DateRange;
|
||||
|
||||
use Seboettg\CiteProc\Rendering\Date\DatePart;
|
||||
use Seboettg\CiteProc\Rendering\Date\DateTime;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class DateRangeYearRenderer
|
||||
* @package Seboettg\CiteProc\Rendering\Date
|
||||
*/
|
||||
class YearRenderer extends DateRangeRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ArrayList<DatePart> $dateParts
|
||||
* @param DateTime $from
|
||||
* @param DateTime $to
|
||||
* @param $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function parseDateRange(ArrayList $dateParts, DateTime $from, DateTime $to, $delimiter)
|
||||
{
|
||||
$ret = "";
|
||||
foreach ($dateParts as $key => $datePart) {
|
||||
if (strpos($key, "year") !== false) {
|
||||
$ret .= $this->renderOneRangePart($datePart, $from, $to, $delimiter);
|
||||
}
|
||||
if (strpos($key, "month") !== false) {
|
||||
$day = !empty($d = $from->getMonth()) ? $d : "";
|
||||
$ret .= $day;
|
||||
}
|
||||
if (strpos($key, "day") !== false) {
|
||||
$day = !empty($d = $from->getDay()) ? $datePart->render($from, $this->parentDateObject) : "";
|
||||
$ret .= $day;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Date;
|
||||
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Seboettg\CiteProc\Exception\InvalidDateTimeException;
|
||||
|
||||
class DateTime extends \DateTime
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $year = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $month = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $day = 0;
|
||||
|
||||
/**
|
||||
* DateTime constructor.
|
||||
* @param string $year
|
||||
* @param string $month
|
||||
* @param string $day
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($year, $month, $day)
|
||||
{
|
||||
try {
|
||||
parent::__construct("$year-$month-$day", new DateTimeZone("Europe/Berlin"));
|
||||
} catch (Exception $e) {
|
||||
throw new InvalidDateTimeException("Could not create valid date with year=$year, month=$month, day=$day.");
|
||||
}
|
||||
|
||||
$this->year = intval(self::format("Y"));
|
||||
$this->month = intval(self::format("n"));
|
||||
$this->day = intval(self::format("j"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $year
|
||||
* @return $this
|
||||
*/
|
||||
public function setYear($year)
|
||||
{
|
||||
$this->year = $year;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $month
|
||||
* @return $this
|
||||
*/
|
||||
public function setMonth($month)
|
||||
{
|
||||
$this->month = $month;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $day
|
||||
* @return $this
|
||||
*/
|
||||
public function setDay($day)
|
||||
{
|
||||
$this->day = $day;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $year
|
||||
* @param int $month
|
||||
* @param int $day
|
||||
* @return $this
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function setDate($year, $month, $day)
|
||||
{
|
||||
$this->year = $year;
|
||||
$this->month = $month;
|
||||
$this->day = $day;
|
||||
parent::setDate($year, $month, $day);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getArray()
|
||||
{
|
||||
return [$this->year, $this->month, $this->day];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getYear()
|
||||
{
|
||||
return $this->year;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMonth()
|
||||
{
|
||||
return $this->month;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDay()
|
||||
{
|
||||
return $this->day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function renderNumeric()
|
||||
{
|
||||
$ret = $this->year;
|
||||
$ret .= $this->month > 0 && $this->month < 13 ? "-".sprintf("%02s", $this->month) : "";
|
||||
$ret .= $this->day > 0 && $this->day < 32 ? "-".sprintf("%02s", $this->day) : "";
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
Vendored
+198
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
|
||||
use Seboettg\CiteProc\Styles\DelimiterTrait;
|
||||
use Seboettg\CiteProc\Styles\DisplayTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Group
|
||||
* The cs:group rendering element must contain one or more rendering elements (with the exception of cs:layout).
|
||||
* cs:group may carry the delimiter attribute to separate its child elements, as well as affixes and display attributes
|
||||
* (applied to the output of the group as a whole) and formatting attributes (transmitted to the enclosed elements).
|
||||
* cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least one
|
||||
* rendering element in cs:group calls a variable (either directly or via a macro), and b) all variables that are
|
||||
* called are empty. This accommodates descriptive cs:text elements.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Group implements Rendering, HasParent
|
||||
{
|
||||
use DelimiterTrait,
|
||||
AffixesTrait,
|
||||
DisplayTrait,
|
||||
FormattingTrait,
|
||||
ConsecutivePunctuationCharacterTrait;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* cs:group may carry the delimiter attribute to separate its child elements
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $delimiter = "";
|
||||
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $renderedChildsWithVariable = [];
|
||||
|
||||
|
||||
/**
|
||||
* Group constructor.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
* @param $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->children = new ArrayList();
|
||||
foreach ($node->children() as $child) {
|
||||
$this->children->append(Factory::create($child, $this));
|
||||
}
|
||||
$this->initDisplayAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initDelimiterAttributes($node);
|
||||
$this->initFormattingAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$textParts = [];
|
||||
$terms = $variables = $haveVariables = $elementCount = 0;
|
||||
foreach ($this->children as $child) {
|
||||
$elementCount++;
|
||||
|
||||
if (($child instanceof Text)
|
||||
&& ($child->getSource() == 'term'
|
||||
|| $child->getSource() == 'value')
|
||||
) {
|
||||
++$terms;
|
||||
}
|
||||
|
||||
if (($child instanceof Label)) {
|
||||
++$terms;
|
||||
}
|
||||
if (method_exists($child, "getSource") && $child->getSource() == 'variable'
|
||||
&& !empty($child->getVariable()) && $child->getVariable() != "date"
|
||||
&& !empty($data->{$child->getVariable()})
|
||||
) {
|
||||
++$variables;
|
||||
}
|
||||
|
||||
$text = $child->render($data, $citationNumber);
|
||||
$delimiter = $this->delimiter;
|
||||
if (!empty($text)) {
|
||||
if ($delimiter && ($elementCount < count($this->children))) {
|
||||
//check to see if the delimiter is already the last character of the text string
|
||||
//if so, remove it so we don't have two of them when the group will be merged
|
||||
$stext = strip_tags(trim($text));
|
||||
if ((strrpos($stext, $delimiter[0]) + 1) == strlen($stext) && strlen($stext) > 1) {
|
||||
$text = str_replace($stext, '----REPLACE----', $text);
|
||||
$stext = substr($stext, 0, -1);
|
||||
$text = str_replace('----REPLACE----', $stext, $text);
|
||||
}
|
||||
}
|
||||
$textParts[] = $text;
|
||||
|
||||
if (method_exists($child, "getSource") && $child->getSource() == 'variable'
|
||||
|| (method_exists(
|
||||
$child,
|
||||
"getVariable"
|
||||
) && $child->getVariable() != "date" && !empty($child->getVariable()))
|
||||
) {
|
||||
$haveVariables++;
|
||||
}
|
||||
|
||||
if (method_exists($child, "getSource") && $child->getSource() == 'macro') {
|
||||
$haveVariables++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->formatting($textParts, $variables, $haveVariables, $terms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $textParts
|
||||
* @param $variables
|
||||
* @param $haveVariables
|
||||
* @param $terms
|
||||
* @return string
|
||||
*/
|
||||
protected function formatting($textParts, $variables, $haveVariables, $terms)
|
||||
{
|
||||
if (empty($textParts)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if ($variables && !$haveVariables) {
|
||||
return ""; // there has to be at least one other none empty value before the term is output
|
||||
}
|
||||
|
||||
if (count($textParts) == $terms) {
|
||||
return ""; // there has to be at least one other none empty value before the term is output
|
||||
}
|
||||
|
||||
$text = StringHelper::implodeAndPreventConsecutiveChars($this->delimiter, $textParts);
|
||||
|
||||
if (!empty($text)) {
|
||||
return $this->wrapDisplayBlock($this->addAffixes($this->format(($text))));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDelimiter()
|
||||
{
|
||||
return !empty($this->delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
/**
|
||||
* Interface HasParent
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
interface HasParent
|
||||
{
|
||||
public function getParent();
|
||||
}
|
||||
Vendored
+262
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Label
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Label implements Rendering
|
||||
{
|
||||
use AffixesTrait,
|
||||
FormattingTrait,
|
||||
TextCaseTrait;
|
||||
|
||||
private $variable;
|
||||
|
||||
/**
|
||||
* Selects the form of the term, with allowed values:
|
||||
*
|
||||
* - “long” - (default), e.g. “page”/”pages” for the “page” term
|
||||
* - “short” - e.g. “p.”/”pp.” for the “page” term
|
||||
* - “symbol” - e.g. “§”/”§§” for the “section” term
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $form = "";
|
||||
|
||||
/**
|
||||
* Sets pluralization of the term, with allowed values:
|
||||
*
|
||||
* - “contextual” - (default), the term plurality matches that of the variable content. Content is considered
|
||||
* plural when it contains multiple numbers (e.g. “page 1”, “pages 1-3”, “volume 2”, “volumes 2 & 4”), or, in
|
||||
* the case of the “number-of-pages” and “number-of-volumes” variables, when the number is higher than 1
|
||||
* (“1 volume” and “3 volumes”).
|
||||
* - “always” - always use the plural form, e.g. “pages 1” and “pages 1-3”
|
||||
* - “never” - always use the singular form, e.g. “page 1” and “page 1-3”
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $plural = "contextual";
|
||||
|
||||
/**
|
||||
* Label constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case "variable":
|
||||
$this->variable = (string) $attribute;
|
||||
break;
|
||||
case "form":
|
||||
$this->form = (string) $attribute;
|
||||
break;
|
||||
case "plural":
|
||||
$this->plural = (string) $attribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en';
|
||||
|
||||
$text = '';
|
||||
$variables = explode(' ', $this->variable);
|
||||
$form = !empty($this->form) ? $this->form : 'long';
|
||||
$plural = $this->defaultPlural();
|
||||
|
||||
if ($this->variable === "editortranslator") {
|
||||
if (isset($data->editor) && isset($data->translator)) {
|
||||
$plural = $this->getPlural($data, $plural, "editortranslator");
|
||||
$term = CiteProc::getContext()->getLocale()->filter('terms', "editortranslator", $form);
|
||||
$pluralForm = $term->{$plural};
|
||||
if (!empty($pluralForm)) {
|
||||
$text = $pluralForm;
|
||||
}
|
||||
}
|
||||
} elseif ($this->variable === "locator") {
|
||||
$citationItem = CiteProc::getContext()->getCitationItemById($data->id);
|
||||
if (!empty($citationItem->label)) {
|
||||
$plural = $this->evaluateStringPluralism($citationItem->locator, $citationItem->label);
|
||||
$term = CiteProc::getContext()->getLocale()->filter('terms', $citationItem->label, $form);
|
||||
$pluralForm = $term->{$plural} ?? "";
|
||||
if (!empty($citationItem->locator) && !empty($pluralForm)) {
|
||||
$text = $pluralForm;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($variables as $variable) {
|
||||
if (isset($data->{$variable})) {
|
||||
$plural = $this->getPlural($data, $plural, $variable);
|
||||
$term = CiteProc::getContext()->getLocale()->filter('terms', $variable, $form);
|
||||
$pluralForm = $term->{$plural} ?? "";
|
||||
if (!empty($data->{$variable}) && !empty($pluralForm)) {
|
||||
$text = $pluralForm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formatting($text, $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @param string $variable
|
||||
* @return string
|
||||
*/
|
||||
private function evaluateStringPluralism($str, $variable)
|
||||
{
|
||||
$plural = 'single';
|
||||
if (!empty($str)) {
|
||||
switch ($variable) {
|
||||
case 'page':
|
||||
case 'chapter':
|
||||
case 'folio':
|
||||
$pageRegex = "/([a-zA-Z]*)([0-9]+)\s*(?:–|-)\s*([a-zA-Z]*)([0-9]+)/";
|
||||
$err = preg_match($pageRegex, $str, $m);
|
||||
if ($err !== false && count($m) == 0) {
|
||||
$plural = 'single';
|
||||
} elseif ($err !== false && count($m)) {
|
||||
$plural = 'multiple';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_numeric($str)) {
|
||||
return $str > 1 ? 'multiple' : 'single';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $variable
|
||||
*/
|
||||
public function setVariable($variable)
|
||||
{
|
||||
$this->variable = $variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $plural
|
||||
* @param $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function getPlural($data, $plural, $variable)
|
||||
{
|
||||
|
||||
if ($variable === "editortranslator" && isset($data->editor)) {
|
||||
$var = $data->editor;
|
||||
} else {
|
||||
$var = $data->{$variable};
|
||||
}
|
||||
if (((!isset($this->plural) || empty($plural))) && !empty($var)) {
|
||||
if (is_array($var)) {
|
||||
$count = count($var);
|
||||
if ($count == 1) {
|
||||
$plural = 'single';
|
||||
return $plural;
|
||||
} elseif ($count > 1) {
|
||||
$plural = 'multiple';
|
||||
return $plural;
|
||||
}
|
||||
return $plural;
|
||||
} else {
|
||||
return $this->evaluateStringPluralism($data->{$variable}, $variable);
|
||||
}
|
||||
} else {
|
||||
if ($this->plural != "always") {
|
||||
$plural = $this->evaluateStringPluralism($data->{$variable}, $variable);
|
||||
return $plural;
|
||||
}
|
||||
return $plural;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $form
|
||||
*/
|
||||
public function setForm($form)
|
||||
{
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @param $lang
|
||||
* @return string
|
||||
*/
|
||||
protected function formatting($text, $lang)
|
||||
{
|
||||
if (empty($text)) {
|
||||
return "";
|
||||
}
|
||||
if ($this->stripPeriods) {
|
||||
$text = str_replace('.', '', $text);
|
||||
}
|
||||
|
||||
$text = preg_replace("/\s&\s/", " & ", $text); //replace ampersands by html entity
|
||||
$text = $this->format($this->applyTextCase($text, $lang));
|
||||
return $this->addAffixes($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function defaultPlural()
|
||||
{
|
||||
$plural = "";
|
||||
switch ($this->plural) {
|
||||
case 'never':
|
||||
$plural = 'single';
|
||||
break;
|
||||
case 'always':
|
||||
$plural = 'multiple';
|
||||
break;
|
||||
case 'contextual':
|
||||
default:
|
||||
}
|
||||
return $plural;
|
||||
}
|
||||
}
|
||||
Vendored
+264
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\RenderingState;
|
||||
use Seboettg\CiteProc\Style\StyleElement;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
|
||||
use Seboettg\CiteProc\Styles\DelimiterTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Util\CiteProcHelper;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Layout
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Layout implements Rendering
|
||||
{
|
||||
private static $numberOfCitedItems = 0;
|
||||
|
||||
use AffixesTrait,
|
||||
FormattingTrait,
|
||||
DelimiterTrait,
|
||||
ConsecutivePunctuationCharacterTrait;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* When used within cs:citation, the delimiter attribute may be used to specify a delimiter for cites within a
|
||||
* citation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $delimiter = "";
|
||||
|
||||
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
* @param StyleElement $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct($node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
self::$numberOfCitedItems = 0;
|
||||
$this->children = new ArrayList();
|
||||
foreach ($node->children() as $child) {
|
||||
$this->children->append(Factory::create($child, $this));
|
||||
}
|
||||
$this->initDelimiterAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initFormattingAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param array|ArrayList $citationItems
|
||||
* @return string|array
|
||||
*/
|
||||
public function render($data, $citationItems = [])
|
||||
{
|
||||
$ret = "";
|
||||
$sorting = CiteProc::getContext()->getSorting();
|
||||
if (!empty($sorting)) {
|
||||
CiteProc::getContext()->setRenderingState(new RenderingState("sorting"));
|
||||
$sorting->sort($data);
|
||||
CiteProc::getContext()->setRenderingState(new RenderingState("rendering"));
|
||||
}
|
||||
|
||||
if (CiteProc::getContext()->isModeBibliography()) {
|
||||
foreach ($data as $citationNumber => $item) {
|
||||
++self::$numberOfCitedItems;
|
||||
CiteProc::getContext()->getResults()->append(
|
||||
$this->wrapBibEntry($item, $this->renderSingle($item, $citationNumber))
|
||||
);
|
||||
}
|
||||
$ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
|
||||
$ret = StringHelper::clearApostrophes($ret);
|
||||
return "<div class=\"csl-bib-body\">".$ret."\n</div>";
|
||||
} elseif (CiteProc::getContext()->isModeCitation()) {
|
||||
if ($citationItems->count() > 0) { //is there a filter for specific citations?
|
||||
if ($this->isGroupedCitations($citationItems)) { //if citation items grouped?
|
||||
return $this->renderGroupedCitations($data, $citationItems);
|
||||
} else {
|
||||
$data = $this->filterCitationItems($data, $citationItems);
|
||||
$ret = $this->renderCitations($data, $ret);
|
||||
}
|
||||
} else {
|
||||
$ret = $this->renderCitations($data, $ret);
|
||||
}
|
||||
}
|
||||
$ret = StringHelper::clearApostrophes($ret);
|
||||
return $this->addAffixes($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
private function renderSingle($data, $citationNumber = null)
|
||||
{
|
||||
$bibliographyOptions = CiteProc::getContext()->getBibliographySpecificOptions();
|
||||
$inMargin = [];
|
||||
$margin = [];
|
||||
foreach ($this->children as $key => $child) {
|
||||
$rendered = $child->render($data, $citationNumber);
|
||||
$this->getChildrenAffixesAndDelimiter($child);
|
||||
if (CiteProc::getContext()->isModeBibliography()
|
||||
&& $bibliographyOptions->getSecondFieldAlign() === "flush"
|
||||
) {
|
||||
if ($key === 0 && !empty($rendered)) {
|
||||
$inMargin[] = $rendered;
|
||||
} else {
|
||||
$margin[] = $rendered;
|
||||
}
|
||||
} else {
|
||||
$inMargin[] = $rendered;
|
||||
}
|
||||
}
|
||||
$inMargin = array_filter($inMargin);
|
||||
$margin = array_filter($margin);
|
||||
if (!empty($inMargin) && !empty($margin) && CiteProc::getContext()->isModeBibliography()) {
|
||||
$leftMargin = $this->removeConsecutiveChars($this->htmlentities($this->format(implode("", $inMargin))));
|
||||
$rightInline = $this->removeConsecutiveChars(
|
||||
$this->htmlentities($this->format(implode("", $margin))).
|
||||
$this->suffix
|
||||
);
|
||||
$res = '<div class="csl-left-margin">' . trim($leftMargin) . '</div>';
|
||||
$res .= '<div class="csl-right-inline">' . trim($rightInline) . '</div>';
|
||||
return $res;
|
||||
} elseif (!empty($inMargin)) {
|
||||
$res = $this->format(implode("", $inMargin));
|
||||
return $this->htmlentities($this->removeConsecutiveChars($res));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public static function getNumberOfCitedItems()
|
||||
{
|
||||
return self::$numberOfCitedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $dataItem
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function wrapBibEntry($dataItem, $value)
|
||||
{
|
||||
$value = $this->addAffixes($value);
|
||||
return "\n ".
|
||||
"<div class=\"csl-entry\">" .
|
||||
$renderedItem = CiteProcHelper::applyAdditionMarkupFunction($dataItem, "csl-entry", $value) .
|
||||
"</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private function htmlentities($text)
|
||||
{
|
||||
$text = preg_replace("/(.*)&([^#38|amp];.*)/u", "$1&$2", $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $ret
|
||||
* @return string
|
||||
*/
|
||||
private function renderCitations($data, $ret)
|
||||
{
|
||||
CiteProc::getContext()->getResults()->replace([]);
|
||||
foreach ($data as $citationNumber => $item) {
|
||||
$renderedItem = $this->renderSingle($item, $citationNumber);
|
||||
$renderedItem = CiteProcHelper::applyAdditionMarkupFunction($item, "csl-entry", $renderedItem);
|
||||
CiteProc::getContext()->getResults()->append($renderedItem);
|
||||
CiteProc::getContext()->appendCitedItem($item);
|
||||
}
|
||||
$ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataList $data
|
||||
* @param ArrayList $citationItems
|
||||
* @return mixed
|
||||
*/
|
||||
private function filterCitationItems($data, $citationItems)
|
||||
{
|
||||
$arr = $data->toArray();
|
||||
|
||||
$arr_ = array_filter($arr, function ($dataItem) use ($citationItems) {
|
||||
foreach ($citationItems as $citationItem) {
|
||||
if ($dataItem->id === $citationItem->id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return $data->replace($arr_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayList $citationItems
|
||||
* @return bool
|
||||
*/
|
||||
private function isGroupedCitations(ArrayList $citationItems)
|
||||
{
|
||||
$firstItem = array_values($citationItems->toArray())[0];
|
||||
if (is_array($firstItem)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataList $data
|
||||
* @param ArrayList $citationItems
|
||||
* @return array|string
|
||||
*/
|
||||
private function renderGroupedCitations($data, $citationItems)
|
||||
{
|
||||
$group = [];
|
||||
foreach ($citationItems as $citationItemGroup) {
|
||||
$data_ = $this->filterCitationItems(clone $data, $citationItemGroup);
|
||||
CiteProc::getContext()->setCitationData($data_);
|
||||
$group[] = $this->addAffixes(StringHelper::clearApostrophes($this->renderCitations($data_, "")));
|
||||
}
|
||||
if (CiteProc::getContext()->isCitationsAsArray()) {
|
||||
return $group;
|
||||
}
|
||||
return implode("\n", $group);
|
||||
}
|
||||
}
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Name;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class EtAl
|
||||
* Et-al abbreviation, controlled via the et-al-... attributes (see Name), can be further customized with the optional
|
||||
* cs:et-al element, which must follow the cs:name element (if present). The term attribute may be set to either “et-al”
|
||||
* (the default) or to “and others” to use either term. The formatting attributes may also be used, for example to
|
||||
* italicize the “et-al” term.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering\Name
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class EtAl implements Rendering
|
||||
{
|
||||
use FormattingTrait;
|
||||
|
||||
private $term;
|
||||
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
/**
|
||||
* @var SimpleXMLElement $attribute
|
||||
*/
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'term':
|
||||
$this->term = (string) $attribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->initFormattingAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList|stdClass $data
|
||||
* @param null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
return $this->format(CiteProc::getContext()->getLocale()->filter('terms', $this->term)->single);
|
||||
}
|
||||
}
|
||||
Vendored
+655
@@ -0,0 +1,655 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Name;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Style\InheritableNameAttributesTrait;
|
||||
use Seboettg\CiteProc\Style\Options\DemoteNonDroppingParticle;
|
||||
use Seboettg\CiteProc\Style\Options\SubsequentAuthorSubstituteRule;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\DelimiterTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Util\CiteProcHelper;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\CiteProc\Util\NameHelper;
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Name
|
||||
*
|
||||
* The cs:name element, an optional child element of cs:names, can be used to describe the formatting of individual
|
||||
* names, and the separation of names within a name variable.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering\Name
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Name implements HasParent
|
||||
{
|
||||
use InheritableNameAttributesTrait,
|
||||
FormattingTrait,
|
||||
AffixesTrait,
|
||||
DelimiterTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $nameParts;
|
||||
|
||||
/**
|
||||
* Specifies the text string used to separate names in a name variable. Default is ”, ” (e.g. “Doe, Smith”).
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $delimiter = ", ";
|
||||
|
||||
/**
|
||||
* @var Names
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement
|
||||
*/
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $etAl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $variable;
|
||||
|
||||
/**
|
||||
* Name constructor.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Names $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, Names $parent)
|
||||
{
|
||||
$this->node = $node;
|
||||
$this->parent = $parent;
|
||||
|
||||
$this->nameParts = [];
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement $child
|
||||
*/
|
||||
foreach ($node->children() as $child) {
|
||||
switch ($child->getName()) {
|
||||
case "name-part":
|
||||
/** @var NamePart $namePart */
|
||||
$namePart = Factory::create($child, $this);
|
||||
$this->nameParts[$namePart->getName()] = $namePart;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'form':
|
||||
$this->form = (string) $attribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initDelimiterAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param string $var
|
||||
* @param integer|null $citationNumber
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function render($data, $var, $citationNumber = null)
|
||||
{
|
||||
$this->variable = $var;
|
||||
$name = $data->{$var};
|
||||
if (!$this->attributesInitialized) {
|
||||
$this->initInheritableNameAttributes($this->node);
|
||||
}
|
||||
if ("text" === $this->and) {
|
||||
$this->and = CiteProc::getContext()->getLocale()->filter('terms', 'and')->single;
|
||||
} elseif ('symbol' === $this->and) {
|
||||
$this->and = '&';
|
||||
}
|
||||
|
||||
$resultNames = $this->handleSubsequentAuthorSubstitution($name, $citationNumber);
|
||||
|
||||
if (empty($resultNames)) {
|
||||
return CiteProc::getContext()->getCitationData()->getSubsequentAuthorSubstitute();
|
||||
}
|
||||
|
||||
$resultNames = $this->prepareAbbreviation($resultNames);
|
||||
|
||||
/* When set to “true” (the default is “false”), name lists truncated by et-al abbreviation are followed by
|
||||
the name delimiter, the ellipsis character, and the last name of the original name list. This is only
|
||||
possible when the original name list has at least two more names than the truncated name list (for this
|
||||
the value of et-al-use-first/et-al-subsequent-min must be at least 2 less than the value of
|
||||
et-al-min/et-al-subsequent-use-first). */
|
||||
if ($this->etAlUseLast && $this->isEtAl($name, $resultNames)) {
|
||||
$this->and = "…"; // set "and"
|
||||
$this->etAl = null; //reset $etAl;
|
||||
}
|
||||
|
||||
/* add "and" */
|
||||
$this->addAnd($resultNames);
|
||||
|
||||
$text = $this->renderDelimiterPrecedesLast($resultNames);
|
||||
|
||||
if (empty($text)) {
|
||||
$text = implode($this->delimiter, $resultNames);
|
||||
}
|
||||
|
||||
$text = $this->appendEtAl($name, $text, $resultNames);
|
||||
|
||||
/* A third value, “count”, returns the total number of names that would otherwise be rendered by the use of the
|
||||
cs:names element (taking into account the effects of et-al abbreviation and editor/translator collapsing),
|
||||
which allows for advanced sorting. */
|
||||
if ($this->form == 'count') {
|
||||
return (int) count($resultNames);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $nameItem
|
||||
* @param int $rank
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function formatName($nameItem, $rank)
|
||||
{
|
||||
$nameObj = $this->cloneNamePOSC($nameItem);
|
||||
|
||||
$useInitials = $this->initialize && !is_null($this->initializeWith) && $this->initializeWith !== false;
|
||||
if ($useInitials && isset($nameItem->given)) {
|
||||
$nameObj->given = StringHelper::initializeBySpaceOrHyphen($nameItem->given, $this->initializeWith);
|
||||
}
|
||||
|
||||
$renderedResult = $this->getNamesString($nameObj, $rank);
|
||||
CiteProcHelper::applyAdditionMarkupFunction($nameItem, $this->parent->getVariables()[0], $renderedResult);
|
||||
return trim($renderedResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $name
|
||||
* @param int $rank
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function getNamesString($name, $rank)
|
||||
{
|
||||
$text = "";
|
||||
|
||||
if (!isset($name->family)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$text = $this->nameOrder($name, $rank);
|
||||
|
||||
//contains nbsp prefixed by normal space or followed by normal space?
|
||||
$text = htmlentities($text);
|
||||
if (strpos($text, " ") !== false || strpos($text, " ") !== false) {
|
||||
$text = preg_replace("/[\s]+/", "", $text); //remove normal spaces
|
||||
return preg_replace("/ +/", " ", $text);
|
||||
}
|
||||
$text = html_entity_decode(preg_replace("/[\s]+/", " ", $text));
|
||||
return $this->format(trim($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $name
|
||||
* @return stdClass
|
||||
*/
|
||||
private function cloneNamePOSC($name)
|
||||
{
|
||||
$nameObj = new stdClass();
|
||||
if (isset($name->family)) {
|
||||
$nameObj->family = $name->family;
|
||||
}
|
||||
if (isset($name->given)) {
|
||||
$nameObj->given = $name->given;
|
||||
}
|
||||
if (isset($name->{'non-dropping-particle'})) {
|
||||
$nameObj->{'non-dropping-particle'} = $name->{'non-dropping-particle'};
|
||||
}
|
||||
if (isset($name->{'dropping-particle'})) {
|
||||
$nameObj->{'dropping-particle'} = $name->{'dropping-particle'};
|
||||
}
|
||||
if (isset($name->{'suffix'})) {
|
||||
$nameObj->{'suffix'} = $name->{'suffix'};
|
||||
}
|
||||
return $nameObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $resultNames
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEtAl($data, $resultNames): bool
|
||||
{
|
||||
return count($data) > 1
|
||||
&& !empty($resultNames)
|
||||
&& !empty($this->etAl)
|
||||
&& !empty($this->etAlMin)
|
||||
&& !empty($this->etAlUseFirst)
|
||||
&& count($data) != count($resultNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $text
|
||||
* @param $resultNames
|
||||
* @return string
|
||||
*/
|
||||
protected function appendEtAl($data, $text, $resultNames)
|
||||
{
|
||||
//append et al abbreviation
|
||||
if ($this->isEtAl($data, $resultNames)) {
|
||||
/* By default, when a name list is truncated to a single name, the name and the “et-al” (or “and others”)
|
||||
term are separated by a space (e.g. “Doe et al.”). When a name list is truncated to two or more names, the
|
||||
name delimiter is used (e.g. “Doe, Smith, et al.”). This behavior can be changed with the
|
||||
delimiter-precedes-et-al attribute. */
|
||||
|
||||
switch ($this->delimiterPrecedesEtAl) {
|
||||
case 'never':
|
||||
$text = $text . " " . $this->etAl;
|
||||
break;
|
||||
case 'always':
|
||||
$text = $text . $this->delimiter . $this->etAl;
|
||||
break;
|
||||
case 'contextual':
|
||||
default:
|
||||
if (count($resultNames) === 1) {
|
||||
$text .= " " . $this->etAl;
|
||||
} else {
|
||||
$text .= $this->delimiter . $this->etAl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultNames
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareAbbreviation($resultNames)
|
||||
{
|
||||
$cnt = count($resultNames);
|
||||
/* Use of et-al-min and et-al-user-first enables et-al abbreviation. If the number of names in a name variable
|
||||
matches or exceeds the number set on et-al-min, the rendered name list is truncated after reaching the number of
|
||||
names set on et-al-use-first. */
|
||||
|
||||
if (isset($this->etAlMin) && isset($this->etAlUseFirst)) {
|
||||
if ($this->etAlMin <= $cnt) {
|
||||
if ($this->etAlUseLast && $this->etAlMin - $this->etAlUseFirst >= 2) {
|
||||
/* et-al-use-last: When set to “true” (the default is “false”), name lists truncated by et-al
|
||||
abbreviation are followed by the name delimiter, the ellipsis character, and the last name of the
|
||||
original name list. This is only possible when the original name list has at least two more names
|
||||
than the truncated name list (for this the value of et-al-use-first/et-al-subsequent-min must be at
|
||||
least 2 less than the value of et-al-min/et-al-subsequent-use-first).*/
|
||||
|
||||
$lastName = array_pop($resultNames); //remove last Element and remember in $lastName
|
||||
}
|
||||
for ($i = $this->etAlUseFirst; $i < $cnt; ++$i) {
|
||||
unset($resultNames[$i]);
|
||||
}
|
||||
|
||||
$resultNames = array_values($resultNames);
|
||||
|
||||
if (!empty($lastName)) { // append $lastName if exist
|
||||
$resultNames[] = $lastName;
|
||||
}
|
||||
|
||||
if ($this->parent->hasEtAl()) {
|
||||
$this->etAl = $this->parent->getEtAl()->render(null);
|
||||
return $resultNames;
|
||||
} else {
|
||||
$this->etAl = CiteProc::getContext()->getLocale()->filter('terms', 'et-al')->single;
|
||||
return $resultNames;
|
||||
}
|
||||
}
|
||||
return $resultNames;
|
||||
}
|
||||
return $resultNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param stdClass $preceding
|
||||
* @return array
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
protected function renderSubsequentSubstitution($data, $preceding)
|
||||
{
|
||||
$resultNames = [];
|
||||
$subsequentSubstitution = CiteProc::getContext()->getCitationData()->getSubsequentAuthorSubstitute();
|
||||
$subsequentSubstitutionRule = CiteProc::getContext()->getCitationData()->getSubsequentAuthorSubstituteRule();
|
||||
|
||||
/**
|
||||
* @var string $type
|
||||
* @var stdClass $name
|
||||
*/
|
||||
foreach ($data as $rank => $name) {
|
||||
switch ($subsequentSubstitutionRule) {
|
||||
/* “partial-each” - when one or more rendered names in the name variable match those in the preceding
|
||||
bibliographic entry, the value of subsequent-author-substitute substitutes for each matching name.
|
||||
Matching starts with the first name, and continues up to the first mismatch. */
|
||||
case SubsequentAuthorSubstituteRule::PARTIAL_EACH:
|
||||
if (NameHelper::precedingHasAuthor($preceding, $name)) {
|
||||
$resultNames[] = $subsequentSubstitution;
|
||||
} else {
|
||||
$resultNames[] = $this->formatName($name, $rank);
|
||||
}
|
||||
break;
|
||||
/* “partial-first” - as “partial-each”, but substitution is limited to the first name of the name
|
||||
variable. */
|
||||
case SubsequentAuthorSubstituteRule::PARTIAL_FIRST:
|
||||
if ($rank === 0) {
|
||||
if ($preceding->author[0]->family === $name->family) {
|
||||
$resultNames[] = $subsequentSubstitution;
|
||||
} else {
|
||||
$resultNames[] = $this->formatName($name, $rank);
|
||||
}
|
||||
} else {
|
||||
$resultNames[] = $this->formatName($name, $rank);
|
||||
}
|
||||
break;
|
||||
|
||||
/* “complete-each” - requires a complete match like “complete-all”, but now the value of
|
||||
subsequent-author-substitute substitutes for each rendered name. */
|
||||
case SubsequentAuthorSubstituteRule::COMPLETE_EACH:
|
||||
try {
|
||||
if (NameHelper::identicalAuthors($preceding, $data)) {
|
||||
$resultNames[] = $subsequentSubstitution;
|
||||
} else {
|
||||
$resultNames[] = $this->formatName($name, $rank);
|
||||
}
|
||||
} catch (CiteProcException $e) {
|
||||
$resultNames[] = $this->formatName($name, $rank);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $resultNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param int $citationNumber
|
||||
* @return array
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function handleSubsequentAuthorSubstitution($data, $citationNumber)
|
||||
{
|
||||
$hasPreceding = CiteProc::getContext()->getCitationData()->hasKey($citationNumber - 1);
|
||||
$subsequentSubstitution = CiteProc::getContext()->getCitationData()->getSubsequentAuthorSubstitute();
|
||||
$subsequentSubstitutionRule = CiteProc::getContext()->getCitationData()->getSubsequentAuthorSubstituteRule();
|
||||
$preceding = CiteProc::getContext()->getCitationData()->get($citationNumber - 1);
|
||||
|
||||
|
||||
if ($hasPreceding && !is_null($subsequentSubstitution) && !empty($subsequentSubstitutionRule)) {
|
||||
/**
|
||||
* @var stdClass $preceding
|
||||
*/
|
||||
if ($subsequentSubstitutionRule == SubsequentAuthorSubstituteRule::COMPLETE_ALL) {
|
||||
try {
|
||||
if (NameHelper::identicalAuthors($preceding, $data)) {
|
||||
return [];
|
||||
} else {
|
||||
$resultNames = $this->getFormattedNames($data);
|
||||
}
|
||||
} catch (CiteProcException $e) {
|
||||
$resultNames = $this->getFormattedNames($data);
|
||||
}
|
||||
} else {
|
||||
$resultNames = $this->renderSubsequentSubstitution($data, $preceding);
|
||||
}
|
||||
} else {
|
||||
$resultNames = $this->getFormattedNames($data);
|
||||
}
|
||||
return $resultNames;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
protected function getFormattedNames($data)
|
||||
{
|
||||
$resultNames = [];
|
||||
foreach ($data as $rank => $name) {
|
||||
$formatted = $this->formatName($name, $rank);
|
||||
$resultNames[] = NameHelper::addExtendedMarkup($this->variable, $name, $formatted);
|
||||
}
|
||||
return $resultNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultNames
|
||||
* @return string
|
||||
*/
|
||||
protected function renderDelimiterPrecedesLastNever($resultNames)
|
||||
{
|
||||
$text = "";
|
||||
if (!$this->etAlUseLast) {
|
||||
if (count($resultNames) === 1) {
|
||||
$text = $resultNames[0];
|
||||
} elseif (count($resultNames) === 2) {
|
||||
$text = implode(" ", $resultNames);
|
||||
} else { // >2
|
||||
$lastName = array_pop($resultNames);
|
||||
$text = implode($this->delimiter, $resultNames)." ".$lastName;
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultNames
|
||||
* @return string
|
||||
*/
|
||||
protected function renderDelimiterPrecedesLastContextual($resultNames)
|
||||
{
|
||||
if (count($resultNames) === 1) {
|
||||
$text = $resultNames[0];
|
||||
} elseif (count($resultNames) === 2) {
|
||||
$text = implode(" ", $resultNames);
|
||||
} else {
|
||||
$text = implode($this->delimiter, $resultNames);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultNames
|
||||
*/
|
||||
protected function addAnd(&$resultNames)
|
||||
{
|
||||
$count = count($resultNames);
|
||||
if (!empty($this->and) && $count > 1 && empty($this->etAl)) {
|
||||
$new = $this->and.' '.end($resultNames); // add and-prefix of the last name if "and" is defined
|
||||
// set prefixed last name at the last position of $resultNames array
|
||||
$resultNames[count($resultNames) - 1] = $new;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultNames
|
||||
* @return array|string
|
||||
*/
|
||||
protected function renderDelimiterPrecedesLast($resultNames)
|
||||
{
|
||||
$text = "";
|
||||
if (!empty($this->and) && empty($this->etAl)) {
|
||||
switch ($this->delimiterPrecedesLast) {
|
||||
case 'after-inverted-name':
|
||||
//TODO: implement
|
||||
break;
|
||||
case 'always':
|
||||
$text = implode($this->delimiter, $resultNames);
|
||||
break;
|
||||
case 'never':
|
||||
$text = $this->renderDelimiterPrecedesLastNever($resultNames);
|
||||
break;
|
||||
case 'contextual':
|
||||
default:
|
||||
$text = $this->renderDelimiterPrecedesLastContextual($resultNames);
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param integer $rank
|
||||
*
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function nameOrder($data, $rank)
|
||||
{
|
||||
$nameAsSortOrder = (($this->nameAsSortOrder === "first" && $rank === 0) || $this->nameAsSortOrder === "all");
|
||||
$demoteNonDroppingParticle = CiteProc::getContext()->getGlobalOptions()->getDemoteNonDroppingParticles();
|
||||
$normalizedName = NameHelper::normalizeName($data);
|
||||
if (StringHelper::isLatinString($normalizedName) || StringHelper::isCyrillicString($normalizedName)) {
|
||||
if ($this->form === "long"
|
||||
&& $nameAsSortOrder
|
||||
&& ((string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::NEVER
|
||||
|| (string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::SORT_ONLY)
|
||||
) {
|
||||
// [La] [Fontaine], [Jean] [de], [III]
|
||||
NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
|
||||
NameHelper::appendParticleTo($data, "given", "dropping-particle");
|
||||
|
||||
list($family, $given) = $this->renderNameParts($data);
|
||||
|
||||
$text = $family.(!empty($given) ? $this->sortSeparator.$given : "");
|
||||
$text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix : "";
|
||||
} elseif ($this->form === "long"
|
||||
&& $nameAsSortOrder
|
||||
&& (is_null($demoteNonDroppingParticle)
|
||||
|| (string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::DISPLAY_AND_SORT)
|
||||
) {
|
||||
// [Fontaine], [Jean] [de] [La], [III]
|
||||
NameHelper::appendParticleTo($data, "given", "dropping-particle");
|
||||
NameHelper::appendParticleTo($data, "given", "non-dropping-particle");
|
||||
list($family, $given) = $this->renderNameParts($data);
|
||||
$text = $family;
|
||||
$text .= !empty($given) ? $this->sortSeparator.$given : "";
|
||||
$text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix : "";
|
||||
} elseif ($this->form === "long" && $nameAsSortOrder && empty($demoteNonDroppingParticle)) {
|
||||
list($family, $given) = $this->renderNameParts($data);
|
||||
$text = $family;
|
||||
$text .= !empty($given) ? $this->delimiter.$given : "";
|
||||
$text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix : "";
|
||||
} elseif ($this->form === "short") {
|
||||
// [La] [Fontaine]
|
||||
NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
|
||||
$text = $data->family;
|
||||
} else {// form "long" (default)
|
||||
// [Jean] [de] [La] [Fontaine] [III]
|
||||
NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
|
||||
NameHelper::prependParticleTo($data, "family", "dropping-particle");
|
||||
NameHelper::appendParticleTo($data, "family", "suffix");
|
||||
list($family, $given) = $this->renderNameParts($data);
|
||||
$text = !empty($given) ? $given." ".$family : $family;
|
||||
}
|
||||
} elseif (StringHelper::isAsianString(NameHelper::normalizeName($data))) {
|
||||
$text = $this->form === "long" ? $data->family . $data->given : $data->family;
|
||||
} else {
|
||||
$text = $this->form === "long" ? $data->family . " " . $data->given : $data->family;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
private function renderNameParts($data)
|
||||
{
|
||||
$given = "";
|
||||
if (array_key_exists("family", $this->nameParts)) {
|
||||
$family = $this->nameParts["family"]->render($data);
|
||||
} else {
|
||||
$family = $data->family;
|
||||
}
|
||||
if (isset($data->given)) {
|
||||
if (array_key_exists("given", $this->nameParts)) {
|
||||
$given = $this->nameParts["given"]->render($data);
|
||||
} else {
|
||||
$given = $data->given;
|
||||
}
|
||||
}
|
||||
return [$family, $given];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function isNameAsSortOrder()
|
||||
{
|
||||
return $this->nameAsSortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $delimiter
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Names
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Name;
|
||||
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class NamePart
|
||||
*
|
||||
* The cs:name element may contain one or two cs:name-part child elements for name-part-specific formatting.
|
||||
* cs:name-part must carry the name attribute, set to either “given” or “family”.
|
||||
*
|
||||
* If set to “given”, formatting and text-case attributes on cs:name-part affect the “given” and “dropping-particle”
|
||||
* name-parts. affixes surround the “given” name-part, enclosing any demoted name particles for inverted names.
|
||||
*
|
||||
* If set to “family”, formatting and text-case attributes affect the “family” and “non-dropping-particle” name-parts.
|
||||
* affixes surround the “family” name-part, enclosing any preceding name particles, as well as the “suffix” name-part
|
||||
* for non-inverted names.
|
||||
*
|
||||
* The “suffix” name-part is not subject to name-part formatting. The use of cs:name-part elements does not influence
|
||||
* which, or in what order, name-parts are rendered.
|
||||
*
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering\Name
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class NamePart
|
||||
{
|
||||
|
||||
use FormattingTrait,
|
||||
TextCaseTrait,
|
||||
AffixesTrait;
|
||||
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var Name
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* NamePart constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Name $parent
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
if ($attribute->getName() === 'name') {
|
||||
$this->name = (string) $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return mixed
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function render($data)
|
||||
{
|
||||
if (!isset($data->{$this->name})) {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch ($this->name) {
|
||||
/* If set to “given”, formatting and text-case attributes on cs:name-part affect the “given” and
|
||||
“dropping-particle” name-parts. affixes surround the “given” name-part, enclosing any demoted name particles
|
||||
for inverted names.*/
|
||||
case 'given':
|
||||
return $this->addAffixes($this->format($this->applyTextCase($data->given)));
|
||||
|
||||
/* if name set to “family”, formatting and text-case attributes affect the “family” and
|
||||
“non-dropping-particle” name-parts. affixes surround the “family” name-part, enclosing any preceding name
|
||||
particles, as well as the “suffix” name-part for non-inverted names.*/
|
||||
case 'family':
|
||||
return $this->addAffixes($this->format($this->applyTextCase($data->family)));
|
||||
}
|
||||
throw new CiteProcException("This shouldn't happen.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
Vendored
+422
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Name;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Rendering\Label;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\CiteProc\Rendering\Term\Punctuation;
|
||||
use Seboettg\CiteProc\RenderingState;
|
||||
use Seboettg\CiteProc\Style\InheritableNameAttributesTrait;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\DelimiterTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\CiteProc\Util\NameHelper;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Names
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering\Name
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Names implements Rendering, HasParent
|
||||
{
|
||||
use DelimiterTrait,
|
||||
AffixesTrait,
|
||||
FormattingTrait,
|
||||
InheritableNameAttributesTrait;
|
||||
|
||||
/**
|
||||
* Variables (selected with the required variable attribute), each of which can contain multiple names (e.g. the
|
||||
* “author” variable contains all the author names of the cited item). If multiple variables are selected
|
||||
* (separated by single spaces, see example below), each variable is independently rendered in the order specified.
|
||||
*
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $variables;
|
||||
|
||||
/**
|
||||
* The Name element, an optional child element of Names, can be used to describe the formatting of individual
|
||||
* names, and the separation of names within a name variable.
|
||||
*
|
||||
* @var Name
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* The optional Label element must be included after the Name and EtAl elements, but before
|
||||
* the Substitute element. When used as a child element of Names, Label does not carry the variable
|
||||
* attribute; it uses the variable(s) set on the parent Names element instead.
|
||||
*
|
||||
* @var Label
|
||||
*/
|
||||
private $label;
|
||||
|
||||
/**
|
||||
* The optional Substitute element, which must be included as the last child element of Names, adds
|
||||
* substitution in case the name variables specified in the parent cs:names element are empty. The substitutions
|
||||
* are specified as child elements of Substitute, and must consist of one or more rendering elements (with the
|
||||
* exception of Layout). A shorthand version of Names without child elements, which inherits the attributes
|
||||
* values set on the cs:name and EtAl child elements of the original Names element, may also be used. If
|
||||
* Substitute contains multiple child elements, the first element to return a non-empty result is used for
|
||||
* substitution. Substituted variables are suppressed in the rest of the output to prevent duplication. An example,
|
||||
* where an empty “author” name variable is substituted by the “editor” name variable, or, when no editors exist,
|
||||
* by the “title” macro:
|
||||
*
|
||||
* <macro name="author">
|
||||
* <names variable="author">
|
||||
* <substitute>
|
||||
* <names variable="editor"/>
|
||||
* <text macro="title"/>
|
||||
* </substitute>
|
||||
* </names>
|
||||
* </macro>
|
||||
*
|
||||
* @var Substitute
|
||||
*/
|
||||
private $substitute;
|
||||
|
||||
/**
|
||||
* Et-al abbreviation, controlled via the et-al-... attributes (see Name), can be further customized with the
|
||||
* optional cs:et-al element, which must follow the cs:name element (if present). The term attribute may be set to
|
||||
* either “et-al” (the default) or to “and others” to use either term. The formatting attributes may also be used,
|
||||
* for example to italicize the “et-al” term:
|
||||
*
|
||||
* @var EtAl
|
||||
*/
|
||||
private $etAl;
|
||||
|
||||
/**
|
||||
* The delimiter attribute may be set on cs:names to separate the names of the different name variables (e.g. the
|
||||
* semicolon in “Doe, Smith (editors); Johnson (translator)”).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $delimiter = ", ";
|
||||
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $renderLabelBeforeName = false;
|
||||
|
||||
/**
|
||||
* Names constructor.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
* @param $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->initInheritableNameAttributes($node);
|
||||
$this->parent = $parent;
|
||||
/**
|
||||
* @var SimpleXMLElement $child
|
||||
*/
|
||||
|
||||
foreach ($node->children() as $child) {
|
||||
switch ($child->getName()) {
|
||||
case "name":
|
||||
$this->name = Factory::create($child, $this);
|
||||
if ($this->label !== null) {
|
||||
$this->renderLabelBeforeName = true;
|
||||
}
|
||||
break;
|
||||
case "label":
|
||||
$this->label = Factory::create($child);
|
||||
break;
|
||||
case "substitute":
|
||||
$this->substitute = new Substitute($child, $this);
|
||||
break;
|
||||
case "et-al":
|
||||
$this->etAl = Factory::create($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement $attribute
|
||||
*/
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
if ("variable" === $attribute->getName()) {
|
||||
$this->variables = new ArrayList(...explode(" ", (string) $attribute));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initDelimiterAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initFormattingAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* This outputs the contents of one or more name variables (selected with the required variable attribute), each
|
||||
* of which can contain multiple names (e.g. the “author” variable contains all the author names of the cited item).
|
||||
* If multiple variables are selected (separated by single spaces), each variable is independently rendered in the
|
||||
* order specified, with one exception: when the selection consists of “editor” and “translator”, and when the
|
||||
* contents of these two name variables is identical, then the contents of only one name variable is rendered. In
|
||||
* addition, the “editortranslator” term is used if the Names element contains a Label element, replacing the
|
||||
* default “editor” and “translator” terms (e.g. resulting in “Doe (editor & translator)”).
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$str = "";
|
||||
|
||||
/* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
|
||||
is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
|
||||
term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
|
||||
“translator” terms (e.g. resulting in “Doe (editor & translator)”) */
|
||||
if ($this->variables->hasElement("editor") && $this->variables->hasElement("translator")) {
|
||||
if (isset($data->editor)
|
||||
&& isset($data->translator) && NameHelper::sameNames($data->editor, $data->translator)
|
||||
) {
|
||||
if (isset($this->name)) {
|
||||
$str .= $this->name->render($data, 'editor');
|
||||
} else {
|
||||
$arr = [];
|
||||
foreach ($data->editor as $editor) {
|
||||
$edt = $this->format($editor->family.", ".$editor->given);
|
||||
$results[] = NameHelper::addExtendedMarkup('editor', $editor, $edt);
|
||||
}
|
||||
$str .= implode($this->delimiter, $arr);
|
||||
}
|
||||
if (isset($this->label)) {
|
||||
$this->label->setVariable("editortranslator");
|
||||
$str .= $this->label->render($data);
|
||||
}
|
||||
$vars = $this->variables->toArray();
|
||||
$vars = array_filter($vars, function ($value) {
|
||||
return !($value === "editor" || $value === "translator");
|
||||
});
|
||||
$this->variables->setArray($vars);
|
||||
}
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($this->variables as $var) {
|
||||
if (!empty($data->{$var})) {
|
||||
if (!empty($this->name)) {
|
||||
$res = $this->name->render($data, $var, $citationNumber);
|
||||
$name = $res;
|
||||
if (!empty($this->label)) {
|
||||
$name = $this->appendLabel($data, $var, $name);
|
||||
}
|
||||
//add multiple counting values
|
||||
if (is_numeric($name) && $this->name->getForm() === "count") {
|
||||
$results = $this->addCountValues($res, $results);
|
||||
} else {
|
||||
$results[] = $this->format($name);
|
||||
}
|
||||
} else {
|
||||
foreach ($data->{$var} as $name) {
|
||||
$formatted = $this->format($name->given." ".$name->family);
|
||||
$results[] = NameHelper::addExtendedMarkup($var, $name, $formatted);
|
||||
}
|
||||
}
|
||||
// suppress substituted variables
|
||||
if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
|
||||
unset($data->{$var});
|
||||
}
|
||||
} else {
|
||||
if (!empty($this->substitute)) {
|
||||
$results[] = $this->substitute->render($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
$results = $this->filterEmpty($results);
|
||||
$str .= implode($this->delimiter, $results);
|
||||
return !empty($str) ? $this->addAffixes($str) : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $var
|
||||
* @param $name
|
||||
* @return string
|
||||
*/
|
||||
private function appendLabel($data, $var, $name): string
|
||||
{
|
||||
$this->label->setVariable($var);
|
||||
$renderedLabel = trim($this->label->render($data));
|
||||
if (empty($renderedLabel)) {
|
||||
return $name;
|
||||
}
|
||||
if ($this->renderLabelBeforeName) {
|
||||
$delimiter = !in_array(
|
||||
trim($this->label->renderSuffix()),
|
||||
Punctuation::getAllPunctuations()
|
||||
) ? " " : "";
|
||||
$result = $renderedLabel . $delimiter . trim($name);
|
||||
} else {
|
||||
$delimiter = !in_array(
|
||||
trim($this->label->renderPrefix()),
|
||||
Punctuation::getAllPunctuations()
|
||||
) ? " " : "";
|
||||
$result = trim($name) . $delimiter . $renderedLabel;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $res
|
||||
* @param $results
|
||||
* @return array
|
||||
*/
|
||||
private function addCountValues($res, $results)
|
||||
{
|
||||
$lastElement = current($results);
|
||||
$key = key($results);
|
||||
if (!empty($lastElement)) {
|
||||
$lastElement += $res;
|
||||
$results[$key] = $lastElement;
|
||||
} else {
|
||||
$results[] = $res;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEtAl()
|
||||
{
|
||||
return !empty($this->etAl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EtAl
|
||||
*/
|
||||
public function getEtAl()
|
||||
{
|
||||
return $this->etAl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EtAl $etAl
|
||||
* @return $this
|
||||
*/
|
||||
public function setEtAl(EtAl $etAl)
|
||||
{
|
||||
$this->etAl = $etAl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName()
|
||||
{
|
||||
return !empty($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Name $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setName(Name $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getVariables()
|
||||
{
|
||||
return $this->variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLabel()
|
||||
{
|
||||
return !empty($this->label);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Label
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Label $label
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
private function filterEmpty(array $results)
|
||||
{
|
||||
return array_filter($results, function ($item) {
|
||||
return !empty($item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isRenderLabelBeforeName(): bool
|
||||
{
|
||||
return $this->renderLabelBeforeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $renderLabelBeforeName
|
||||
*/
|
||||
public function setRenderLabelBeforeName(bool $renderLabelBeforeName): void
|
||||
{
|
||||
$this->renderLabelBeforeName = $renderLabelBeforeName;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Name;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\CiteProc\RenderingState;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Substitute
|
||||
* The optional cs:substitute element, which must be included as the last child element of cs:names, adds substitution
|
||||
* in case the name variables specified in the parent cs:names element are empty. The substitutions are specified as
|
||||
* child elements of cs:substitute, and must consist of one or more rendering elements (with the exception of
|
||||
* cs:layout).
|
||||
*
|
||||
* A shorthand version of cs:names without child elements, which inherits the attributes values set on the cs:name and
|
||||
* cs:et-al child elements of the original cs:names element, may also be used.
|
||||
*
|
||||
* If cs:substitute contains multiple child elements, the first element to return a non-empty result is used for
|
||||
* substitution. Substituted variables are suppressed in the rest of the output to prevent duplication. An example,
|
||||
* where an empty “author” name variable is substituted by the “editor” name variable, or, when no editors exist, by
|
||||
* the “title” macro:
|
||||
* <macro name="author">
|
||||
* <names variable="author">
|
||||
* <substitute>
|
||||
* <names variable="editor"/>
|
||||
* <text macro="title"/>
|
||||
* </substitute>
|
||||
* </names>
|
||||
* </macro>
|
||||
* @package Seboettg\CiteProc\Rendering\Name
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Substitute implements Rendering
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* @var Names
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* Substitute constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Names $parent
|
||||
* @throws InvalidStylesheetException
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, Names $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->children = new ArrayList();
|
||||
foreach ($node->children() as $child) {
|
||||
|
||||
/** @var SimpleXMLElement $child */
|
||||
if ($child->getName() === "names") {
|
||||
|
||||
/** @var Names $names */
|
||||
$names = Factory::create($child, $this);
|
||||
|
||||
/* A shorthand version of cs:names without child elements, which inherits the attributes values set on
|
||||
the cs:name and cs:et-al child elements of the original cs:names element, may also be used. */
|
||||
if (!$names->hasEtAl()) {
|
||||
// inherit et-al
|
||||
if ($this->parent->hasEtAl()) {
|
||||
$names->setEtAl($this->parent->getEtAl());
|
||||
}
|
||||
}
|
||||
if (!$names->hasName()) {
|
||||
// inherit name
|
||||
if ($this->parent->hasName()) {
|
||||
$names->setName($this->parent->getName());
|
||||
}
|
||||
}
|
||||
// inherit label
|
||||
if (!$names->hasLabel() && $this->parent->hasLabel()) {
|
||||
$names->setLabel($this->parent->getLabel());
|
||||
}
|
||||
|
||||
$this->children->append($names);
|
||||
} else {
|
||||
$object = Factory::create($child, $this);
|
||||
$this->children->append($object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$ret = [];
|
||||
if (CiteProc::getContext()->getRenderingState()->getValue() !== RenderingState::SORTING) {
|
||||
CiteProc::getContext()->setRenderingState(new RenderingState(RenderingState::SUBSTITUTION));
|
||||
}
|
||||
|
||||
/** @var Rendering $child */
|
||||
foreach ($this->children as $child) {
|
||||
/* If cs:substitute contains multiple child elements, the first element to return a
|
||||
non-empty result is used for substitution. */
|
||||
$res = $child->render($data, $citationNumber);
|
||||
if (!empty($res)) {
|
||||
$ret[] = $res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
|
||||
CiteProc::getContext()->setRenderingState(new RenderingState(RenderingState::RENDERING));
|
||||
}
|
||||
return implode("", $ret);
|
||||
}
|
||||
}
|
||||
Vendored
+225
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\DisplayTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use Seboettg\CiteProc\Util;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Number
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Number implements Rendering
|
||||
{
|
||||
|
||||
private const RANGE_DELIMITER_HYPHEN = "-";
|
||||
|
||||
private const RANGE_DELIMITER_AMPERSAND = "&";
|
||||
|
||||
private const RANGE_DELIMITER_COMMA = ",";
|
||||
|
||||
private const PATTERN_ORDINAL = "/\s*(\d+)\s*([\-\–&,])\s*(\d+)\s*/";
|
||||
|
||||
private const PATTERN_LONG_ORDINAL = "/\s*(\d+)\s*([\-\–&,])\s*(\d+)\s*/";
|
||||
|
||||
private const PATTERN_ROMAN = "/\s*(\d+)\s*([\-\–&,])\s*(\d+)\s*/";
|
||||
|
||||
private const PATTERN_NUMERIC_DEFAULT = "/\s*(\d+)\s*([\-\–&,])\s*(\d+)\s*/";
|
||||
|
||||
use FormattingTrait,
|
||||
AffixesTrait,
|
||||
TextCaseTrait,
|
||||
DisplayTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $variable;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* Number constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
//<number variable="edition" form="ordinal"/>
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'variable':
|
||||
$this->variable = (string) $attribute;
|
||||
break;
|
||||
case 'form':
|
||||
$this->form = (string) $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null): string
|
||||
{
|
||||
$lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en';
|
||||
|
||||
if (empty($this->variable) || empty($data->{$this->variable})) {
|
||||
return "";
|
||||
}
|
||||
$number = $data->{$this->variable};
|
||||
$decimalNumber = $this->toDecimalNumber($number);
|
||||
switch ($this->form) {
|
||||
case 'ordinal':
|
||||
if (preg_match(self::PATTERN_ORDINAL, $decimalNumber, $matches)) {
|
||||
$num1 = self::ordinal($matches[1]);
|
||||
$num2 = self::ordinal($matches[3]);
|
||||
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]);
|
||||
} else {
|
||||
$text = self::ordinal($decimalNumber);
|
||||
}
|
||||
break;
|
||||
case 'long-ordinal':
|
||||
if (preg_match(self::PATTERN_LONG_ORDINAL, $decimalNumber, $matches)) {
|
||||
if ($this->textCase === "capitalize-first" || $this->textCase === "sentence") {
|
||||
$num1 = self::longOrdinal($matches[1]);
|
||||
$num2 = self::longOrdinal($matches[3]);
|
||||
} else {
|
||||
$num1 = $this->applyTextCase(self::longOrdinal($matches[1]));
|
||||
$num2 = $this->applyTextCase(self::longOrdinal($matches[3]));
|
||||
}
|
||||
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]);
|
||||
} else {
|
||||
$text = self::longOrdinal($decimalNumber);
|
||||
}
|
||||
break;
|
||||
case 'roman':
|
||||
if (preg_match(self::PATTERN_ROMAN, $decimalNumber, $matches)) {
|
||||
$num1 = Util\NumberHelper::dec2roman($matches[1]);
|
||||
$num2 = Util\NumberHelper::dec2roman($matches[3]);
|
||||
$text = $this->buildNumberRangeString($num1, $num2, $matches[2]);
|
||||
} else {
|
||||
$text = Util\NumberHelper::dec2roman($decimalNumber);
|
||||
}
|
||||
break;
|
||||
case 'numeric':
|
||||
default:
|
||||
/*
|
||||
During the extraction, numbers separated by a hyphen are stripped of intervening spaces (“2 - 4”
|
||||
becomes “2-4”). Numbers separated by a comma receive one space after the comma (“2,3” and “2 , 3”
|
||||
become “2, 3”), while numbers separated by an ampersand receive one space before and one after the
|
||||
ampersand (“2&3” becomes “2 & 3”).
|
||||
*/
|
||||
$decimalNumber = $data->{$this->variable};
|
||||
if (preg_match(self::PATTERN_NUMERIC_DEFAULT, $decimalNumber, $matches)) {
|
||||
$text = $this->buildNumberRangeString($matches[1], $matches[3], $matches[2]);
|
||||
} else {
|
||||
$text = $decimalNumber;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $this->wrapDisplayBlock($this->addAffixes($this->format($this->applyTextCase($text, $lang))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $num
|
||||
* @return string
|
||||
*/
|
||||
public static function ordinal($num): string
|
||||
{
|
||||
if ((int) ($num / 10) % 10 == 1) {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal')->single;
|
||||
} elseif ($num % 10 == 1) {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-01')->single;
|
||||
} elseif ($num % 10 == 2) {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-02')->single;
|
||||
} elseif ($num % 10 == 3) {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-03')->single;
|
||||
} else {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal-04')->single;
|
||||
}
|
||||
if (empty($ordinalSuffix)) {
|
||||
$ordinalSuffix = CiteProc::getContext()->getLocale()->filter('terms', 'ordinal')->single;
|
||||
}
|
||||
return $num . $ordinalSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $num
|
||||
* @return string
|
||||
*/
|
||||
public static function longOrdinal($num)
|
||||
{
|
||||
$num = sprintf("%02d", $num);
|
||||
$ret = CiteProc::getContext()->getLocale()->filter('terms', 'long-ordinal-'.$num)->single;
|
||||
if (!$ret) {
|
||||
return self::ordinal($num);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $num1
|
||||
* @param string|int $num2
|
||||
* @param string $delim
|
||||
* @return string
|
||||
*/
|
||||
public function buildNumberRangeString($num1, $num2, $delim)
|
||||
{
|
||||
|
||||
if (self::RANGE_DELIMITER_AMPERSAND === $delim) {
|
||||
$numRange = "$num1 ".htmlentities(self::RANGE_DELIMITER_AMPERSAND)." $num2";
|
||||
} else {
|
||||
if (self::RANGE_DELIMITER_COMMA === $delim) {
|
||||
$numRange = $num1.htmlentities(self::RANGE_DELIMITER_COMMA)." $num2";
|
||||
} else {
|
||||
$numRange = $num1.self::RANGE_DELIMITER_HYPHEN.$num2;
|
||||
}
|
||||
}
|
||||
return $numRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $number
|
||||
* @return string
|
||||
*/
|
||||
private function toDecimalNumber($number)
|
||||
{
|
||||
$decimalNumber = $number;
|
||||
if (Util\NumberHelper::isRomanNumber($number)) {
|
||||
$decimalNumber = Util\NumberHelper::roman2Dec($number);
|
||||
} else {
|
||||
$number = mb_strtolower($number);
|
||||
if (preg_match(Util\NumberHelper::PATTERN_ROMAN_RANGE, $number, $matches)) {
|
||||
$num1 = Util\NumberHelper::roman2Dec(mb_strtoupper($matches[1]));
|
||||
$num2 = Util\NumberHelper::roman2Dec(mb_strtoupper($matches[3]));
|
||||
$decimalNumber = sprintf('%d%s%d', $num1, $matches[2], $num2);
|
||||
}
|
||||
}
|
||||
return $decimalNumber;
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Interface RenderingInterface
|
||||
*
|
||||
* Defines "render" function.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*/
|
||||
interface Rendering
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array|DataList|stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = []);
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering\Term;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
class Punctuation extends Enum
|
||||
{
|
||||
public const OPEN_QUOTE = "open-quote";
|
||||
public const CLOSE_QUOTE = "close-quote";
|
||||
public const OPEN_INNER_QUOTE = "open-inner-quote";
|
||||
public const CLOSE_INNER_QUOTE = "close-inner-quote";
|
||||
public const PAGE_RANGE_DELIMITER = "page-range-delimiter";
|
||||
public const COLON = "colon";
|
||||
public const COMMA = "comma";
|
||||
public const SEMICOLON = "semicolon";
|
||||
|
||||
public static function getAllPunctuations(): array
|
||||
{
|
||||
$values = new ArrayList();
|
||||
return $values
|
||||
->setArray(Punctuation::toArray())
|
||||
->map(function (string $punctuation) {
|
||||
return CiteProc::getContext()->getLocale()->filter("terms", $punctuation)->single;
|
||||
})
|
||||
->collect(function ($items) {
|
||||
return array_values($items);
|
||||
});
|
||||
}
|
||||
}
|
||||
Vendored
+294
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Rendering;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\RenderingState;
|
||||
use Seboettg\CiteProc\Styles\AffixesTrait;
|
||||
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
|
||||
use Seboettg\CiteProc\Styles\DisplayTrait;
|
||||
use Seboettg\CiteProc\Styles\FormattingTrait;
|
||||
use Seboettg\CiteProc\Styles\QuotesTrait;
|
||||
use Seboettg\CiteProc\Styles\TextCaseTrait;
|
||||
use Seboettg\CiteProc\Terms\Locator;
|
||||
use Seboettg\CiteProc\Util\CiteProcHelper;
|
||||
use Seboettg\CiteProc\Util\NumberHelper;
|
||||
use Seboettg\CiteProc\Util\PageHelper;
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
use function Seboettg\CiteProc\ucfirst;
|
||||
|
||||
/**
|
||||
* Class Term
|
||||
*
|
||||
* @package Seboettg\CiteProc\Node\Style
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Text implements Rendering
|
||||
{
|
||||
use FormattingTrait,
|
||||
AffixesTrait,
|
||||
TextCaseTrait,
|
||||
DisplayTrait,
|
||||
ConsecutivePunctuationCharacterTrait,
|
||||
QuotesTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $toRenderType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $toRenderTypeValue;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $form = "long";
|
||||
|
||||
/**
|
||||
* Text constructor.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
$name = $attribute->getName();
|
||||
if (in_array($name, ['value', 'variable', 'macro', 'term'])) {
|
||||
$this->toRenderType = $name;
|
||||
$this->toRenderTypeValue = (string) $attribute;
|
||||
}
|
||||
if ($name === "form") {
|
||||
$this->form = (string) $attribute;
|
||||
}
|
||||
}
|
||||
$this->initFormattingAttributes($node);
|
||||
$this->initDisplayAttributes($node);
|
||||
$this->initTextCaseAttributes($node);
|
||||
$this->initAffixesAttributes($node);
|
||||
$this->initQuotesAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$lang = (isset($data->language) && $data->language != 'en') ? $data->language : 'en';
|
||||
|
||||
$renderedText = "";
|
||||
switch ($this->toRenderType) {
|
||||
case 'value':
|
||||
$renderedText = $this->applyTextCase($this->toRenderTypeValue, $lang);
|
||||
break;
|
||||
case 'variable':
|
||||
if ($this->toRenderTypeValue === "locator" && CiteProc::getContext()->isModeCitation()) {
|
||||
$renderedText = $this->renderLocator($data, $citationNumber);
|
||||
// for test sort_BibliographyCitationNumberDescending.json
|
||||
} elseif ($this->toRenderTypeValue === "citation-number") {
|
||||
$renderedText = $this->renderCitationNumber($data, $citationNumber);
|
||||
break;
|
||||
} elseif (in_array($this->toRenderTypeValue, ["page", "chapter-number", "folio"])) {
|
||||
$renderedText = !empty($data->{$this->toRenderTypeValue}) ?
|
||||
$this->renderPage($data->{$this->toRenderTypeValue}) : '';
|
||||
} else {
|
||||
$renderedText = $this->renderVariable($data, $lang);
|
||||
}
|
||||
if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
|
||||
unset($data->{$this->toRenderTypeValue});
|
||||
}
|
||||
if (!CiteProcHelper::isUsingAffixesByMarkupExtentsion($data, $this->toRenderTypeValue)) {
|
||||
$renderedText = $this->applyAdditionalMarkupFunction($data, $renderedText);
|
||||
}
|
||||
break;
|
||||
case 'macro':
|
||||
$renderedText = $this->renderMacro($data);
|
||||
break;
|
||||
case 'term':
|
||||
$term = CiteProc::getContext()
|
||||
->getLocale()
|
||||
->filter("terms", $this->toRenderTypeValue, $this->form)
|
||||
->single;
|
||||
$renderedText = !empty($term) ? $this->applyTextCase($term, $lang) : "";
|
||||
}
|
||||
if (!empty($renderedText)) {
|
||||
$renderedText = $this->formatRenderedText($data, $renderedText);
|
||||
}
|
||||
return $renderedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->toRenderType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVariable()
|
||||
{
|
||||
return $this->toRenderTypeValue;
|
||||
}
|
||||
|
||||
private function renderPage($page)
|
||||
{
|
||||
if (preg_match(NumberHelper::PATTERN_COMMA_AMPERSAND_RANGE, $page)) {
|
||||
$page = $this->normalizeDateRange($page);
|
||||
$ranges = preg_split("/[-–]/", trim($page));
|
||||
if (count($ranges) > 1) {
|
||||
if (!empty(CiteProc::getContext()->getGlobalOptions())
|
||||
&& !empty(CiteProc::getContext()->getGlobalOptions()->getPageRangeFormat())
|
||||
) {
|
||||
return PageHelper::processPageRangeFormats(
|
||||
$ranges,
|
||||
CiteProc::getContext()->getGlobalOptions()->getPageRangeFormat()
|
||||
);
|
||||
}
|
||||
list($from, $to) = $ranges;
|
||||
return $from . "–" . $to;
|
||||
}
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
private function renderLocator($data, $citationNumber)
|
||||
{
|
||||
$citationItem = CiteProc::getContext()->getCitationItemById($data->id);
|
||||
if (!empty($citationItem->label)) {
|
||||
$locatorData = new stdClass();
|
||||
$propertyName = Locator::mapLocatorLabelToRenderVariable($citationItem->label);
|
||||
$locatorData->{$propertyName} = trim($citationItem->locator);
|
||||
$renderTypeValueTemp = $this->toRenderTypeValue;
|
||||
$this->toRenderTypeValue = $propertyName;
|
||||
$result = $this->render($locatorData, $citationNumber);
|
||||
$this->toRenderTypeValue = $renderTypeValueTemp;
|
||||
return $result;
|
||||
}
|
||||
return isset($citationItem->locator) ? trim($citationItem->locator) : '';
|
||||
}
|
||||
|
||||
private function normalizeDateRange($page)
|
||||
{
|
||||
if (preg_match("/^(\d+)\s?--?\s?(\d+)$/", trim($page), $matches)) {
|
||||
return $matches[1]."-".$matches[2];
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $renderedText
|
||||
* @return mixed
|
||||
*/
|
||||
private function applyAdditionalMarkupFunction($data, $renderedText)
|
||||
{
|
||||
return CiteProcHelper::applyAdditionMarkupFunction($data, $this->toRenderTypeValue, $renderedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $lang
|
||||
* @return string
|
||||
*/
|
||||
private function renderVariable($data, $lang)
|
||||
{
|
||||
// check if there is an attribute with prefix short or long e.g. shortTitle or longAbstract
|
||||
// test case group_ShortOutputOnly.json
|
||||
$value = "";
|
||||
if (in_array($this->form, ["short", "long"])) {
|
||||
$attrWithPrefix = $this->form . ucfirst($this->toRenderTypeValue);
|
||||
$attrWithSuffix = $this->toRenderTypeValue . "-" . $this->form;
|
||||
if (isset($data->{$attrWithPrefix}) && !empty($data->{$attrWithPrefix})) {
|
||||
$value = $data->{$attrWithPrefix};
|
||||
} else {
|
||||
if (isset($data->{$attrWithSuffix}) && !empty($data->{$attrWithSuffix})) {
|
||||
$value = $data->{$attrWithSuffix};
|
||||
} else {
|
||||
if (isset($data->{$this->toRenderTypeValue})) {
|
||||
$value = $data->{$this->toRenderTypeValue};
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!empty($data->{$this->toRenderTypeValue})) {
|
||||
$value = $data->{$this->toRenderTypeValue};
|
||||
}
|
||||
}
|
||||
return $this->applyTextCase(
|
||||
StringHelper::clearApostrophes(
|
||||
htmlspecialchars($value, ENT_HTML5)
|
||||
),
|
||||
$lang
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $renderedText
|
||||
* @return string
|
||||
*/
|
||||
private function formatRenderedText($data, $renderedText)
|
||||
{
|
||||
$text = $this->format($renderedText);
|
||||
$res = $this->addAffixes($text);
|
||||
if (CiteProcHelper::isUsingAffixesByMarkupExtentsion($data, $this->toRenderTypeValue)) {
|
||||
$res = $this->applyAdditionalMarkupFunction($data, $res);
|
||||
}
|
||||
if (!empty($res)) {
|
||||
$res = $this->removeConsecutiveChars($res);
|
||||
}
|
||||
$res = $this->addSurroundingQuotes($res);
|
||||
return $this->wrapDisplayBlock($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $citationNumber
|
||||
* @return int|mixed
|
||||
*/
|
||||
private function renderCitationNumber($data, $citationNumber)
|
||||
{
|
||||
$renderedText = $citationNumber + 1;
|
||||
if (!CiteProcHelper::isUsingAffixesByMarkupExtentsion($data, $this->toRenderTypeValue)) {
|
||||
$renderedText = $this->applyAdditionalMarkupFunction($data, $renderedText);
|
||||
}
|
||||
return $renderedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
private function renderMacro($data)
|
||||
{
|
||||
$macro = CiteProc::getContext()->getMacro($this->toRenderTypeValue);
|
||||
if (is_null($macro)) {
|
||||
try {
|
||||
throw new CiteProcException("Macro \"".$this->toRenderTypeValue."\" does not exist.");
|
||||
} catch (CiteProcException $e) {
|
||||
$renderedText = "";
|
||||
}
|
||||
} else {
|
||||
$renderedText = $macro->render($data);
|
||||
}
|
||||
return $renderedText;
|
||||
}
|
||||
}
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
/**
|
||||
* RenderingState defines the mode in which mode the processor currently works.
|
||||
* There are three modes:
|
||||
* - Rendering
|
||||
* - Sorting
|
||||
* - Substitution
|
||||
*
|
||||
* @package Seboettg\CiteProc
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
* @method static RENDERING()
|
||||
* @method static SORTING()
|
||||
* @method static SUBSTITUTION()
|
||||
*/
|
||||
class RenderingState extends Enum
|
||||
{
|
||||
const RENDERING = "rendering";
|
||||
|
||||
const SORTING = "sorting";
|
||||
|
||||
const SUBSTITUTION = "substitution";
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Root;
|
||||
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
class Info
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $authors;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $links;
|
||||
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
$this->authors = [];
|
||||
$this->links = [];
|
||||
|
||||
/** @var SimpleXMLElement $child */
|
||||
foreach ($node->children() as $child) {
|
||||
switch ($child->getName()) {
|
||||
case 'author':
|
||||
case 'contributor':
|
||||
$author = new stdClass();
|
||||
/** @var SimpleXMLElement $authorNode */
|
||||
foreach ($child->children() as $authorNode) {
|
||||
$author->{$authorNode->getName()} = (string) $authorNode;
|
||||
}
|
||||
$this->authors[] = $author;
|
||||
break;
|
||||
case 'link':
|
||||
foreach ($child->attributes() as $attribute) {
|
||||
if ($attribute->getName() === "value") {
|
||||
$this->links[] = (string) $attribute;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->{$child->getName()} = (string) $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthors()
|
||||
{
|
||||
return $this->authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLinks()
|
||||
{
|
||||
return $this->links;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Root;
|
||||
|
||||
use Seboettg\CiteProc\Style\InheritableNameAttributesTrait;
|
||||
|
||||
/**
|
||||
* Class Root
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Root
|
||||
{
|
||||
use InheritableNameAttributesTrait;
|
||||
}
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use Seboettg\CiteProc\Style\Options\BibliographyOptions;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Bibliography
|
||||
*
|
||||
* The cs:bibliography element describes the formatting of bibliographies, which list one or more bibliographic sources.
|
||||
* The required cs:layout child element describes how each bibliographic entry should be formatted. cs:layout may be
|
||||
* preceded by a cs:sort element, which can be used to specify how references within the bibliography should be sorted
|
||||
* (see Sorting).
|
||||
*
|
||||
* @package Seboettg\CiteProc
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Bibliography extends StyleElement
|
||||
{
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* Bibliography constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Root $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, Root $parent)
|
||||
{
|
||||
parent::__construct($node, $parent);
|
||||
$this->node = $node;
|
||||
$bibliographyOptions = new BibliographyOptions($node);
|
||||
CiteProc::getContext()->setBibliographySpecificOptions($bibliographyOptions);
|
||||
$this->initInheritableNameAttributes($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
if (!$this->attributesInitialized) {
|
||||
$this->initInheritableNameAttributes($this->node);
|
||||
}
|
||||
$subsequentAuthorSubstitute = CiteProc::getContext()
|
||||
->getBibliographySpecificOptions()
|
||||
->getSubsequentAuthorSubstitute();
|
||||
|
||||
$subsequentAuthorSubstituteRule = CiteProc::getContext()
|
||||
->getBibliographySpecificOptions()
|
||||
->getSubsequentAuthorSubstituteRule();
|
||||
|
||||
if ($subsequentAuthorSubstitute !== null && !empty($subsequentAuthorSubstituteRule)) {
|
||||
CiteProc::getContext()
|
||||
->getCitationData()
|
||||
->setSubsequentAuthorSubstitute($subsequentAuthorSubstitute);
|
||||
CiteProc::getContext()
|
||||
->getCitationData()
|
||||
->setSubsequentAuthorSubstituteRule($subsequentAuthorSubstituteRule);
|
||||
}
|
||||
return $this->layout->render($data, $citationNumber);
|
||||
}
|
||||
}
|
||||
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Style\Options\CitationOptions;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Citation
|
||||
*
|
||||
* The cs:citation element describes the formatting of citations, which consist of one or more references (“cites”) to
|
||||
* bibliographic sources. Citations appear in the form of either in-text citations (in the author (e.g. “[Doe]”),
|
||||
* author-date (“[Doe 1999]”), label (“[doe99]”) or number (“[1]”) format) or notes. The required cs:layout child
|
||||
* element describes what, and how, bibliographic data should be included in the citations (see Layout).
|
||||
*
|
||||
* @package Seboettg\CiteProc\Node\Style
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Citation extends StyleElement
|
||||
{
|
||||
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* Citation constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @param $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
parent::__construct($node, $parent);
|
||||
$citationOptions = new CitationOptions($node);
|
||||
CiteProc::getContext()->setCitationSpecificOptions($citationOptions);
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param ArrayList $citationItems
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationItems)
|
||||
{
|
||||
if (!$this->attributesInitialized) {
|
||||
$this->initInheritableNameAttributes($this->node);
|
||||
}
|
||||
return $this->layout->render($data, $citationItems);
|
||||
}
|
||||
}
|
||||
+651
@@ -0,0 +1,651 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Rendering\Name\Name;
|
||||
use Seboettg\CiteProc\Rendering\Name\Names;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class InheritableNameAttributesTrait
|
||||
*
|
||||
* Attributes for the cs:names and cs:name elements may also be set on cs:style, cs:citation and cs:bibliography. This
|
||||
* eliminates the need to repeat the same attributes and attribute values for every occurrence of the cs:names and
|
||||
* cs:name elements.
|
||||
*
|
||||
* The available inheritable attributes for cs:name are and, delimiter-precedes-et-al, delimiter-precedes-last,
|
||||
* et-al-min, et-al-use-first, et-al-use-last, et-al-subsequent-min, et-al-subsequent-use-first, initialize,
|
||||
* initialize-with, name-as-sort-order and sort-separator. The attributes name-form and name-delimiter correspond to the
|
||||
* form and delimiter attributes on cs:name. Similarly, names-delimiter corresponds to the delimiter attribute on
|
||||
* cs:names.
|
||||
*
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait InheritableNameAttributesTrait
|
||||
{
|
||||
public static $attributes = [
|
||||
'and',
|
||||
'delimiter-precedes-et-al',
|
||||
'delimiter-precedes-last',
|
||||
'et-al-min',
|
||||
'et-al-use-first',
|
||||
'et-al-use-last',
|
||||
'et-al-subsequent-min',
|
||||
'et-al-subsequent-use-first',
|
||||
'initialize',
|
||||
'initialize-with',
|
||||
'name-as-sort-order',
|
||||
'sort-separator',
|
||||
'name-form',
|
||||
'form',
|
||||
'name-delimiter',
|
||||
'delimiter'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $attributesInitialized = false;
|
||||
|
||||
/**
|
||||
* Specifies the delimiter between the second to last and last name of the names in a name variable. Allowed values
|
||||
* are “text” (selects the “and” term, e.g. “Doe, Johnson and Smith”) and “symbol” (selects the ampersand,
|
||||
* e.g. “Doe, Johnson & Smith”).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $and;
|
||||
|
||||
/**
|
||||
* Determines when the name delimiter or a space is used between a truncated name list and the “et-al”
|
||||
* (or “and others”) term in case of et-al abbreviation. Allowed values:
|
||||
* - “contextual” - (default), name delimiter is only used for name lists truncated to two or more names
|
||||
* - 1 name: “J. Doe et al.”
|
||||
* - 2 names: “J. Doe, S. Smith, et al.”
|
||||
* - “after-inverted-name” - name delimiter is only used if the preceding name is inverted as a result of the
|
||||
* - name-as-sort-order attribute. E.g. with name-as-sort-order set to “first”:
|
||||
* - “Doe, J., et al.”
|
||||
* - “Doe, J., S. Smith et al.”
|
||||
* - “always” - name delimiter is always used
|
||||
* - 1 name: “J. Doe, et al.”
|
||||
* - 2 names: “J. Doe, S. Smith, et al.”
|
||||
* - “never” - name delimiter is never used
|
||||
* - 1 name: “J. Doe et al.”
|
||||
* - 2 names: “J. Doe, S. Smith et al.”
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $delimiterPrecedesEtAl;
|
||||
|
||||
/**
|
||||
* Determines when the name delimiter is used to separate the second to last and the last name in name lists (if
|
||||
* and is not set, the name delimiter is always used, regardless of the value of delimiter-precedes-last). Allowed
|
||||
* values:
|
||||
*
|
||||
* - “contextual” - (default), name delimiter is only used for name lists with three or more names
|
||||
* - 2 names: “J. Doe and T. Williams”
|
||||
* - 3 names: “J. Doe, S. Smith, and T. Williams”
|
||||
* - “after-inverted-name” - name delimiter is only used if the preceding name is inverted as a result of the
|
||||
* name-as-sort-order attribute. E.g. with name-as-sort-order set to “first”:
|
||||
* - “Doe, J., and T. Williams”
|
||||
* - “Doe, J., S. Smith and T. Williams”
|
||||
* - “always” - name delimiter is always used
|
||||
* - 2 names: “J. Doe, and T. Williams”
|
||||
* - 3 names: “J. Doe, S. Smith, and T. Williams”
|
||||
* - “never” - name delimiter is never used
|
||||
* - 2 names: “J. Doe and T. Williams”
|
||||
* - 3 names: “J. Doe, S. Smith and T. Williams”
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $delimiterPrecedesLast;
|
||||
|
||||
/**
|
||||
* Use of etAlMin (et-al-min attribute) and etAlUseFirst (et-al-use-first attribute) enables et-al abbreviation. If
|
||||
* the number of names in a name variable matches or exceeds the number set on etAlMin, the rendered name list is
|
||||
* truncated after reaching the number of names set on etAlUseFirst.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $etAlMin;
|
||||
|
||||
/**
|
||||
* Use of etAlMin (et-al-min attribute) and etAlUseFirst (et-al-use-first attribute) enables et-al abbreviation. If
|
||||
* the number of names in a name variable matches or exceeds the number set on etAlMin, the rendered name list is
|
||||
* truncated after reaching the number of names set on etAlUseFirst.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $etAlUseFirst;
|
||||
|
||||
/**
|
||||
* When set to “true” (the default is “false”), name lists truncated by et-al abbreviation are followed by the name
|
||||
* delimiter, the ellipsis character, and the last name of the original name list. This is only possible when the
|
||||
* original name list has at least two more names than the truncated name list (for this the value of
|
||||
* et-al-use-first/et-al-subsequent-min must be at least 2 less than the value of
|
||||
* et-al-min/et-al-subsequent-use-first).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $etAlUseLast = false;
|
||||
|
||||
/**
|
||||
* If used, the values of these attributes (et-al-subsequent-min and et-al-subsequent-use-first) replace those of
|
||||
* respectively et-al-min and et-al-use-first for subsequent cites (cites referencing earlier cited items).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $etAlSubsequentMin;
|
||||
|
||||
/**
|
||||
* If used, the values of these attributes (et-al-subsequent-min and et-al-subsequent-use-first) replace those of
|
||||
* respectively et-al-min and et-al-use-first for subsequent cites (cites referencing earlier cited items).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $etAlSubsequentUseFirst;
|
||||
|
||||
/**
|
||||
* When set to “false” (the default is “true”), given names are no longer initialized when “initialize-with” is set.
|
||||
* However, the value of “initialize-with” is still added after initials present in the full name (e.g. with
|
||||
* initialize set to “false”, and initialize-with set to ”.”, “James T Kirk” becomes “James T. Kirk”).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $initialize = true;
|
||||
|
||||
/**
|
||||
* When set, given names are converted to initials. The attribute value is added after each initial (”.” results
|
||||
* in “J.J. Doe”). For compound given names (e.g. “Jean-Luc”), hyphenation of the initials can be controlled with
|
||||
* the global initialize-with-hyphen option
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $initializeWith = false;
|
||||
|
||||
/**
|
||||
* Specifies that names should be displayed with the given name following the family name (e.g. “John Doe” becomes
|
||||
* “Doe, John”). The attribute has two possible values:
|
||||
* - “first” - attribute only has an effect on the first name of each name variable
|
||||
* - “all” - attribute has an effect on all names
|
||||
* Note that even when name-as-sort-order changes the name-part order, the display order is not necessarily the same
|
||||
* as the sorting order for names containing particles and suffixes (see Name-part order). Also, name-as-sort-order
|
||||
* only affects names written in the latin or Cyrillic alphabets. Names written in other alphabets (e.g. Asian
|
||||
* scripts) are always displayed with the family name preceding the given name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $nameAsSortOrder = "";
|
||||
|
||||
/**
|
||||
* Sets the delimiter for name-parts that have switched positions as a result of name-as-sort-order. The default
|
||||
* value is ”, ” (“Doe, John”). As is the case for name-as-sort-order, this attribute only affects names written in
|
||||
* the latin or Cyrillic alphabets.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $sortSeparator = ", ";
|
||||
|
||||
/**
|
||||
* Specifies whether all the name-parts of personal names should be displayed (value “long”, the default), or only
|
||||
* the family name and the non-dropping-particle (value “short”). A third value, “count”, returns the total number
|
||||
* of names that would otherwise be rendered by the use of the cs:names element (taking into account the effects of
|
||||
* et-al abbreviation and editor/translator collapsing), which allows for advanced sorting.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $form;
|
||||
|
||||
private $nameForm = "long";
|
||||
|
||||
private $nameDelimiter = ", ";
|
||||
|
||||
public function isDescendantOfMacro()
|
||||
{
|
||||
$parent = $this->parent;
|
||||
|
||||
while ($parent != null && $parent instanceof HasParent) {
|
||||
if ($parent instanceof Macro) {
|
||||
return true;
|
||||
}
|
||||
$parent = $parent->getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function initInheritableNameAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
$context = CiteProc::getContext();
|
||||
$parentStyleElement = null;
|
||||
$root = $context->getRoot();
|
||||
if ($this instanceof Name || $this instanceof Names) {
|
||||
if ($context->getMode() === "bibliography") {
|
||||
$parentStyleElement = $context->getBibliography();
|
||||
} else {
|
||||
$parentStyleElement = $context->getCitation();
|
||||
}
|
||||
} elseif ($this instanceof StyleElement) {
|
||||
$parentStyleElement = $root;
|
||||
}
|
||||
|
||||
foreach (self::$attributes as $nameAttribute) {
|
||||
$attribute = $node[$nameAttribute];
|
||||
switch ($nameAttribute) {
|
||||
case 'and':
|
||||
if (!empty($attribute)) {
|
||||
$this->setAnd((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getAnd())) {
|
||||
$this->setAnd($parentStyleElement->getAnd());
|
||||
} elseif (!empty($root) && !empty($root->getAnd())) {
|
||||
$this->setAnd($root->getAnd());
|
||||
}
|
||||
break;
|
||||
case 'delimiter-precedes-et-al':
|
||||
if (!empty($attribute)) {
|
||||
$this->setDelimiterPrecedesEtAl((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getDelimiterPrecedesEtAl())) {
|
||||
$this->setDelimiterPrecedesEtAl($parentStyleElement->getDelimiterPrecedesEtAl());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setDelimiterPrecedesEtAl($root->getDelimiterPrecedesEtAl());
|
||||
}
|
||||
break;
|
||||
case 'delimiter-precedes-last':
|
||||
if (!empty($attribute)) {
|
||||
$this->setDelimiterPrecedesLast((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getDelimiterPrecedesLast())) {
|
||||
$this->setDelimiterPrecedesLast($parentStyleElement->getDelimiterPrecedesLast());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setDelimiterPrecedesLast($root->getDelimiterPrecedesLast());
|
||||
}
|
||||
break;
|
||||
case 'et-al-min':
|
||||
if (!empty($attribute)) {
|
||||
$this->setEtAlMin(intval((string) $attribute));
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlMin())) {
|
||||
$this->setEtAlMin($parentStyleElement->getEtAlMin());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setEtAlMin($root->getEtAlMin());
|
||||
}
|
||||
break;
|
||||
case 'et-al-use-first':
|
||||
if (!empty($attribute)) {
|
||||
$this->setEtAlUseFirst(intval((string) $attribute));
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlUseFirst())) {
|
||||
$this->setEtAlUseFirst($parentStyleElement->getEtAlUseFirst());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setEtAlUseFirst($root->getEtAlUseFirst());
|
||||
}
|
||||
break;
|
||||
case 'et-al-subsequent-min':
|
||||
if (!empty($attribute)) {
|
||||
$this->setEtAlSubsequentMin(intval((string) $attribute));
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlSubsequentMin())) {
|
||||
$this->setEtAlSubsequentMin($parentStyleElement->getEtAlSubsequentMin());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setEtAlSubsequentMin($root->getEtAlSubsequentMin());
|
||||
}
|
||||
break;
|
||||
case 'et-al-subsequent-use-first':
|
||||
if (!empty($attribute)) {
|
||||
$this->etAlSubsequentUseFirst = intval((string) $attribute);
|
||||
$this->setEtAlSubsequentUseFirst(intval((string) $attribute));
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlSubsequentUseFirst())) {
|
||||
$this->setEtAlSubsequentUseFirst($parentStyleElement->getEtAlSubsequentUseFirst());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setEtAlSubsequentUseFirst($root->getEtAlSubsequentUseFirst());
|
||||
}
|
||||
break;
|
||||
case 'et-al-use-last':
|
||||
if (!empty($attribute)) {
|
||||
$this->setEtAlUseLast(((string) $attribute) === "true");
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlUseLast())) {
|
||||
$this->setEtAlUseLast($parentStyleElement->getEtAlUseLast());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setEtAlUseLast($root->getEtAlUseLast());
|
||||
}
|
||||
break;
|
||||
case 'initialize':
|
||||
if (!empty($attribute)) {
|
||||
$this->setInitialize(((string) $attribute) === "true");
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getInitialize())) {
|
||||
$this->setInitialize($parentStyleElement->getInitialize());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setInitialize($root->getInitialize());
|
||||
}
|
||||
break;
|
||||
case 'initialize-with':
|
||||
if (!empty($attribute)) {
|
||||
$this->setInitializeWith((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getInitializeWith())) {
|
||||
$this->setInitializeWith($parentStyleElement->getInitializeWith());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setInitializeWith($root->getInitializeWith());
|
||||
}
|
||||
break;
|
||||
case 'name-as-sort-order':
|
||||
if (!empty($attribute)) {
|
||||
$this->setNameAsSortOrder((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameAsSortOrder())) {
|
||||
$this->setNameAsSortOrder($parentStyleElement->getNameAsSortOrder());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setNameAsSortOrder($root->getNameAsSortOrder());
|
||||
}
|
||||
break;
|
||||
case 'sort-separator':
|
||||
if (!empty($attribute)) {
|
||||
$this->setSortSeparator((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getSortSeparator())) {
|
||||
$this->setSortSeparator($parentStyleElement->getSortSeparator());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setSortSeparator($root->getSortSeparator());
|
||||
}
|
||||
break;
|
||||
case 'name-form':
|
||||
if ($this instanceof Root || $this instanceof StyleElement) {
|
||||
if (!empty($attribute)) {
|
||||
$this->setNameForm((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement)) {
|
||||
$this->setNameForm($parentStyleElement->getNameForm());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'form':
|
||||
if ($this instanceof Name) {
|
||||
if (!empty($attribute)) {
|
||||
$this->setForm((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameForm())) {
|
||||
$this->setForm($parentStyleElement->getNameForm());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setForm($root->getNameForm());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'name-delimiter':
|
||||
if ($this instanceof Root || $this instanceof StyleElement) {
|
||||
if (!empty($attribute)) {
|
||||
$this->setNameDelimiter((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement)) {
|
||||
$this->setNameDelimiter($parentStyleElement->getNameDelimiter());
|
||||
}
|
||||
} else {
|
||||
/* The attributes name-form and name-delimiter correspond to the form and delimiter attributes
|
||||
on cs:name. Similarly, names-delimiter corresponds to the delimiter attribute on cs:names. */
|
||||
if (!empty($attribute)) {
|
||||
$this->nameDelimiter = $this->delimiter = (string) $attribute;
|
||||
} elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameDelimiter())) {
|
||||
$this->nameDelimiter = $this->delimiter = $parentStyleElement->getNameDelimiter();
|
||||
} elseif (!empty($root)) {
|
||||
$this->nameDelimiter = $this->delimiter = $root->getNameDelimiter();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'delimiter':
|
||||
if ($this instanceof Name) {
|
||||
if (!empty($attribute)) {
|
||||
$this->setDelimiter((string) $attribute);
|
||||
} elseif (!empty($parentStyleElement)) {
|
||||
$this->setDelimiter($parentStyleElement->getNameDelimiter());
|
||||
} elseif (!empty($root)) {
|
||||
$this->setDelimiter($root->getNameDelimiter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->attributesInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAnd()
|
||||
{
|
||||
return $this->and;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $and
|
||||
*/
|
||||
public function setAnd($and)
|
||||
{
|
||||
$this->and = $and;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiterPrecedesEtAl()
|
||||
{
|
||||
return $this->delimiterPrecedesEtAl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $delimiterPrecedesEtAl
|
||||
*/
|
||||
public function setDelimiterPrecedesEtAl($delimiterPrecedesEtAl)
|
||||
{
|
||||
$this->delimiterPrecedesEtAl = $delimiterPrecedesEtAl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiterPrecedesLast()
|
||||
{
|
||||
return $this->delimiterPrecedesLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $delimiterPrecedesLast
|
||||
*/
|
||||
public function setDelimiterPrecedesLast($delimiterPrecedesLast)
|
||||
{
|
||||
$this->delimiterPrecedesLast = $delimiterPrecedesLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEtAlMin()
|
||||
{
|
||||
return $this->etAlMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $etAlMin
|
||||
*/
|
||||
public function setEtAlMin($etAlMin)
|
||||
{
|
||||
$this->etAlMin = $etAlMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEtAlUseFirst()
|
||||
{
|
||||
return $this->etAlUseFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $etAlUseFirst
|
||||
*/
|
||||
public function setEtAlUseFirst($etAlUseFirst)
|
||||
{
|
||||
$this->etAlUseFirst = $etAlUseFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getEtAlUseLast()
|
||||
{
|
||||
return $this->etAlUseLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $etAlUseLast
|
||||
*/
|
||||
public function setEtAlUseLast($etAlUseLast)
|
||||
{
|
||||
$this->etAlUseLast = $etAlUseLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEtAlSubsequentMin()
|
||||
{
|
||||
return $this->etAlSubsequentMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $etAlSubsequentMin
|
||||
*/
|
||||
public function setEtAlSubsequentMin($etAlSubsequentMin)
|
||||
{
|
||||
$this->etAlSubsequentMin = $etAlSubsequentMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEtAlSubsequentUseFirst()
|
||||
{
|
||||
return $this->etAlSubsequentUseFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $etAlSubsequentUseFirst
|
||||
*/
|
||||
public function setEtAlSubsequentUseFirst($etAlSubsequentUseFirst)
|
||||
{
|
||||
$this->etAlSubsequentUseFirst = $etAlSubsequentUseFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getInitialize()
|
||||
{
|
||||
return $this->initialize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $initialize
|
||||
*/
|
||||
public function setInitialize($initialize)
|
||||
{
|
||||
$this->initialize = $initialize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getInitializeWith()
|
||||
{
|
||||
return $this->initializeWith;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $initializeWith
|
||||
*/
|
||||
public function setInitializeWith($initializeWith)
|
||||
{
|
||||
$this->initializeWith = $initializeWith;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNameAsSortOrder()
|
||||
{
|
||||
return $this->nameAsSortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $nameAsSortOrder
|
||||
*/
|
||||
public function setNameAsSortOrder($nameAsSortOrder)
|
||||
{
|
||||
$this->nameAsSortOrder = $nameAsSortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSortSeparator()
|
||||
{
|
||||
return $this->sortSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sortSeparator
|
||||
*/
|
||||
public function setSortSeparator($sortSeparator)
|
||||
{
|
||||
$this->sortSeparator = $sortSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $form
|
||||
*/
|
||||
public function setForm($form)
|
||||
{
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNameForm()
|
||||
{
|
||||
return $this->nameForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $nameForm
|
||||
*/
|
||||
public function setNameForm($nameForm)
|
||||
{
|
||||
$this->nameForm = $nameForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNameDelimiter()
|
||||
{
|
||||
return $this->nameDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $nameDelimiter
|
||||
*/
|
||||
public function setNameDelimiter($nameDelimiter)
|
||||
{
|
||||
$this->nameDelimiter = $nameDelimiter;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style;
|
||||
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Rendering\HasParent;
|
||||
use Seboettg\CiteProc\Rendering\Rendering;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
|
||||
use Seboettg\CiteProc\Util\Factory;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Macro
|
||||
*
|
||||
* Macros, defined with cs:macro elements, contain formatting instructions. Macros can be called with cs:text from
|
||||
* within other macros and the cs:layout element of cs:citation and cs:bibliography, and with cs:key from within cs:sort
|
||||
* of cs:citation and cs:bibliography. It is recommended to place macros after any cs:locale elements and before the
|
||||
* cs:citation element.
|
||||
*
|
||||
* Macros are referenced by the value of the required name attribute on cs:macro. The cs:macro element must contain one
|
||||
* or more rendering elements.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Rendering
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Macro implements Rendering, HasParent
|
||||
{
|
||||
use ConsecutivePunctuationCharacterTrait;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var Root
|
||||
*/
|
||||
private $parent;
|
||||
/**
|
||||
* Macro constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Root $parent
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$attr = $node->attributes();
|
||||
if (!isset($attr['name'])) {
|
||||
throw new CiteProcException("Attribute \"name\" needed.");
|
||||
}
|
||||
$this->name = (string) $attr['name'];
|
||||
|
||||
$this->children = new ArrayList();
|
||||
foreach ($node->children() as $child) {
|
||||
$this->children->append(Factory::create($child, $this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|DataList $data
|
||||
* @param int|null $citationNumber
|
||||
* @return string
|
||||
*/
|
||||
public function render($data, $citationNumber = null)
|
||||
{
|
||||
$ret = [];
|
||||
/** @var Rendering $child */
|
||||
foreach ($this->children as $child) {
|
||||
$res = $child->render($data, $citationNumber);
|
||||
$this->getChildrenAffixesAndDelimiter($child);
|
||||
if (!empty($res)) {
|
||||
$ret[] = $res;
|
||||
}
|
||||
}
|
||||
$res = implode("", $ret);
|
||||
if (!empty($res)) {
|
||||
$res = $this->removeConsecutiveChars($res);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Root
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class GlobalOptionsTrait
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class BibliographyOptions
|
||||
{
|
||||
|
||||
/**
|
||||
* If set, the value of this attribute replaces names in a bibliographic entry that also occur in the preceding
|
||||
* entry. The exact method of substitution depends on the value of the subsequent-author-substitute-rule attribute.
|
||||
* Substitution is limited to the names of the first cs:names element rendered. (Bibliography-specific option)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $subsequentAuthorSubstitute;
|
||||
|
||||
/**
|
||||
* Specifies when and how names are substituted as a result of subsequent-author-substitute.
|
||||
* (Bibliography-specific option)
|
||||
*
|
||||
* @var SubsequentAuthorSubstituteRule
|
||||
*/
|
||||
private $subsequentAuthorSubstituteRule;
|
||||
|
||||
/**
|
||||
* If set to “true” (“false” is the default), bibliographic entries are rendered with hanging-indents.
|
||||
* @var string
|
||||
*/
|
||||
private $hangingIndent = false;
|
||||
|
||||
/**
|
||||
* If set, subsequent lines of bibliographic entries are aligned along the second field. With “flush”, the first
|
||||
* field is flush with the margin. With “margin”, the first field is put in the margin, and subsequent lines are
|
||||
* aligned with the margin.
|
||||
* @var string
|
||||
*/
|
||||
private $secondFieldAlign;
|
||||
|
||||
/**
|
||||
* Specifies vertical line distance. Defaults to “1” (single-spacing), and can be set to any positive integer to
|
||||
* specify a multiple of the standard unit of line height (e.g. “2” for double-spacing).
|
||||
* @var string
|
||||
*/
|
||||
private $lineSpacing;
|
||||
|
||||
/**
|
||||
* Specifies vertical distance between bibliographic entries. By default (with a value of “1”), entries are
|
||||
* separated by a single additional line-height (as set by the line-spacing attribute). Can be set to any
|
||||
* non-negative integer to specify a multiple of this amount.
|
||||
* @var string
|
||||
*/
|
||||
private $entrySpacing;
|
||||
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'subsequent-author-substitute':
|
||||
$this->subsequentAuthorSubstitute = (string) $attribute;
|
||||
break;
|
||||
case 'subsequent-author-substitute-rule':
|
||||
$this->subsequentAuthorSubstituteRule = new SubsequentAuthorSubstituteRule((string) $attribute);
|
||||
break;
|
||||
case 'hanging-indent':
|
||||
$this->hangingIndent = "true" === (string) $attribute ? true : false;
|
||||
break;
|
||||
case 'second-field-align':
|
||||
$this->secondFieldAlign = (string) $attribute;
|
||||
break;
|
||||
case 'line-spacing':
|
||||
$this->lineSpacing = (string) $attribute;
|
||||
break;
|
||||
case 'entry-spacing':
|
||||
$this->entrySpacing = (string) $attribute;
|
||||
}
|
||||
}
|
||||
if (empty($this->subsequentAuthorSubstituteRule)) {
|
||||
$this->subsequentAuthorSubstituteRule = new SubsequentAuthorSubstituteRule("complete-all");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSubsequentAuthorSubstitute()
|
||||
{
|
||||
return $this->subsequentAuthorSubstitute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $subsequentAuthorSubstitute
|
||||
*/
|
||||
public function setSubsequentAuthorSubstitute($subsequentAuthorSubstitute)
|
||||
{
|
||||
$this->subsequentAuthorSubstitute = $subsequentAuthorSubstitute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubsequentAuthorSubstituteRule
|
||||
*/
|
||||
public function getSubsequentAuthorSubstituteRule()
|
||||
{
|
||||
return $this->subsequentAuthorSubstituteRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SubsequentAuthorSubstituteRule $subsequentAuthorSubstituteRule
|
||||
*/
|
||||
public function setSubsequentAuthorSubstituteRule($subsequentAuthorSubstituteRule)
|
||||
{
|
||||
$this->subsequentAuthorSubstituteRule = $subsequentAuthorSubstituteRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHangingIndent()
|
||||
{
|
||||
return $this->hangingIndent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hangingIndent
|
||||
*/
|
||||
public function setHangingIndent($hangingIndent)
|
||||
{
|
||||
$this->hangingIndent = $hangingIndent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSecondFieldAlign()
|
||||
{
|
||||
return $this->secondFieldAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $secondFieldAlign
|
||||
*/
|
||||
public function setSecondFieldAlign($secondFieldAlign)
|
||||
{
|
||||
$this->secondFieldAlign = $secondFieldAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLineSpacing()
|
||||
{
|
||||
return $this->lineSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lineSpacing
|
||||
*/
|
||||
public function setLineSpacing($lineSpacing)
|
||||
{
|
||||
$this->lineSpacing = $lineSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntrySpacing()
|
||||
{
|
||||
return $this->entrySpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entrySpacing
|
||||
*/
|
||||
public function setEntrySpacing($entrySpacing)
|
||||
{
|
||||
$this->entrySpacing = $entrySpacing;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class GlobalOptionsTrait
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CitationOptions
|
||||
{
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
//TODO: implement
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
/**
|
||||
* Class DemoteNonDroppingParticle
|
||||
*
|
||||
* Sets the display and sorting behavior of the non-dropping-particle in inverted names (e.g. “Koning, W. de”).
|
||||
* Some names include a particle that should never be demoted. For these cases the particle should just be included in
|
||||
* the family name field, for example for the French general Charles de Gaulle:
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class DemoteNonDroppingParticle extends Enum
|
||||
{
|
||||
/**
|
||||
* “never”: the non-dropping-particle is treated as part of the family name, whereas the dropping-particle is
|
||||
* appended (e.g. “de Koning, W.”, “La Fontaine, Jean de”). The non-dropping-particle is part of the primary sort
|
||||
* key (sort order A, e.g. “de Koning, W.” appears under “D”).
|
||||
*/
|
||||
const NEVER = "never";
|
||||
|
||||
/**
|
||||
* “sort-only”: same display behavior as “never”, but the non-dropping-particle is demoted to a secondary sort key
|
||||
* (sort order B, e.g. “de Koning, W.” appears under “K”).
|
||||
*/
|
||||
const SORT_ONLY = "sort-only";
|
||||
|
||||
/**
|
||||
* “display-and-sort” (default): the dropping and non-dropping-particle are appended (e.g. “Koning, W. de” and
|
||||
* “Fontaine, Jean de La”). For name sorting, all particles are part of the secondary sort key (sort order B, e.g.
|
||||
* “Koning, W. de” appears under “K”).
|
||||
*/
|
||||
const DISPLAY_AND_SORT = "display-and-sort";
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class GlobalOptionsTrait
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class GlobalOptions
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $initializeWithHyphen = true;
|
||||
|
||||
/**
|
||||
* @var PageRangeFormats
|
||||
*/
|
||||
private $pageRangeFormat;
|
||||
|
||||
/**
|
||||
* @var DemoteNonDroppingParticle
|
||||
*/
|
||||
private $demoteNonDroppingParticles;
|
||||
|
||||
/**
|
||||
* GlobalOptions constructor.
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'initialize-with-hyphen':
|
||||
$this->initializeWithHyphen = !("false" === (string) $attribute);
|
||||
break;
|
||||
case 'page-range-format':
|
||||
$this->pageRangeFormat = new PageRangeFormats((string) $attribute);
|
||||
break;
|
||||
case 'demote-non-dropping-particle':
|
||||
$this->demoteNonDroppingParticles = new DemoteNonDroppingParticle((string) $attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether compound given names (e.g. “Jean-Luc”) should be initialized with a hyphen (“J.-L.”, value
|
||||
* “true”, default) or without (“J.L.”, value “false”).
|
||||
* @return bool
|
||||
*/
|
||||
public function isInitializeWithHyphen(): bool
|
||||
{
|
||||
return $this->initializeWithHyphen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates expansion or collapsing of page ranges: “chicago” (“321–28”), “expanded” (e.g. “321–328”),
|
||||
* “minimal” (“321–8”), or “minimal-two” (“321–28”). Delimits page ranges
|
||||
* with the “page-range-delimiter” term (introduced with CSL 1.0.1, and defaults to an en-dash). If the attribute is
|
||||
* not set, page ranges are rendered without reformatting.
|
||||
* @return PageRangeFormats
|
||||
*/
|
||||
public function getPageRangeFormat(): ?PageRangeFormats
|
||||
{
|
||||
return $this->pageRangeFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display and sorting behavior of the non-dropping-particle in inverted names (e.g. “Koning, W. de”).
|
||||
* @return DemoteNonDroppingParticle
|
||||
*/
|
||||
public function getDemoteNonDroppingParticles(): ?DemoteNonDroppingParticle
|
||||
{
|
||||
return $this->demoteNonDroppingParticles;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
/**
|
||||
* Class PageRangeFormats
|
||||
*
|
||||
* Activates expansion or collapsing of page ranges: “chicago” (“321–28”), “expanded” (e.g. “321–328”),
|
||||
* “minimal” (“321–8”), or “minimal-two” (“321–28”) (see also Appendix V - Page Range Formats). Delimits page ranges
|
||||
* with the “page-range-delimiter” term (introduced with CSL 1.0.1, and defaults to an en-dash). If the attribute is
|
||||
* not set, page ranges are rendered without reformatting.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class PageRangeFormats extends Enum
|
||||
{
|
||||
/**
|
||||
* Page ranges are abbreviated according to the Chicago Manual of Style-rules:
|
||||
*
|
||||
* First number Second number Examples
|
||||
* =================================================================================================================
|
||||
* Less than 100 Use all digits 3–10; 71–72
|
||||
* -----------------------------------------------------------------------------------------------------------------
|
||||
* 100 or multiple of 100 Use all digits 100–104; 600–613;
|
||||
* 1100–1123
|
||||
* -----------------------------------------------------------------------------------------------------------------
|
||||
* 101 through 109 (in multiples of 100) Use changed part only, omitting unneeded zeros 107–8; 505–17; 1002–6
|
||||
* -----------------------------------------------------------------------------------------------------------------
|
||||
* 110 through 199 (in multiples of 100) Use two digits, or more as needed 321–25; 415–532;
|
||||
* 11564–68; 13792–803
|
||||
* -----------------------------------------------------------------------------------------------------------------
|
||||
* 4 digits If numbers are four digits long and three
|
||||
* digits change, use all digits
|
||||
*/
|
||||
const CHICAGO = "chicago";
|
||||
|
||||
/**
|
||||
* Abbreviated page ranges are expanded to their non-abbreviated form: 42–45, 321–328, 2787–2816
|
||||
*/
|
||||
const EXPANDED = "expanded";
|
||||
|
||||
/**
|
||||
* All digits repeated in the second number are left out: 42–5, 321–8, 2787–816
|
||||
*/
|
||||
const MINIMAL = "minimal";
|
||||
|
||||
/**
|
||||
* As “minimal”, but at least two digits are kept in the second number when it has two or more digits long.
|
||||
*/
|
||||
const MINIMAL_TWO = "minimal-two";
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Options;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
/**
|
||||
* Class SubsequentAuthorSubstituteRule
|
||||
*
|
||||
* Specifies when and how names are substituted as a result of subsequent-author-substitute.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class SubsequentAuthorSubstituteRule extends Enum
|
||||
{
|
||||
|
||||
/**
|
||||
* “complete-all” - (default), when all rendered names of the name variable match those in the preceding
|
||||
* bibliographic entry, the value of subsequent-author-substitute replaces the entire name list (including
|
||||
* punctuation and terms like “et al” and “and”), except for the affixes set on the cs:names element.
|
||||
*/
|
||||
const COMPLETE_ALL = "complete-all";
|
||||
|
||||
/**
|
||||
* “complete-each” - requires a complete match like “complete-all”, but now the value of
|
||||
* subsequent-author-substitute substitutes for each rendered name.
|
||||
*/
|
||||
const COMPLETE_EACH = "complete-each";
|
||||
|
||||
/**
|
||||
* “partial-each” - when one or more rendered names in the name variable match those in the preceding bibliographic
|
||||
* entry, the value of subsequent-author-substitute substitutes for each matching name. Matching starts with the
|
||||
* first name, and continues up to the first mismatch.
|
||||
*/
|
||||
const PARTIAL_EACH = "partial-each";
|
||||
|
||||
/**
|
||||
* “partial-first” - as “partial-each”, but substitution is limited to the first name of the name variable.
|
||||
*/
|
||||
const PARTIAL_FIRST = "partial-first";
|
||||
}
|
||||
Vendored
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Sort;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Util\Variables;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Key
|
||||
*
|
||||
* The cs:sort element must contain one or more cs:key child elements. The sort key, set as an attribute on cs:key, must
|
||||
* be a variable (see Appendix IV - Variables) or macro name. For each cs:key element, the sort direction can be set to
|
||||
* either “ascending” (default) or “descending” with the sort attribute. The attributes names-min, names-use-first, and
|
||||
* names-use-last may be used to override the values of the corresponding et-al-min/et-al-subsequent-min,
|
||||
* et-al-use-first/et-al-subsequent-use-first and et-al-use-last attributes, and affect all names generated via macros
|
||||
* called by cs:key.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style\Sort
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Key implements SortKey
|
||||
{
|
||||
/**
|
||||
* variable name or macro
|
||||
* @var string
|
||||
*/
|
||||
private $variable;
|
||||
|
||||
/**
|
||||
* the sort direction can be set to either “ascending” (default) or “descending” with the sort attribute
|
||||
* @var string
|
||||
*/
|
||||
private $sort = "ascending";
|
||||
|
||||
/**
|
||||
* macro name
|
||||
* @var string
|
||||
*/
|
||||
private $macro;
|
||||
|
||||
/**
|
||||
* only relevant for date ranges
|
||||
* @var int
|
||||
*/
|
||||
private $rangePart = 1;
|
||||
|
||||
/**
|
||||
* Key constructor.
|
||||
* The cs:sort element must contain one or more cs:key child elements. The sort key, set as an attribute on cs:key,
|
||||
* must be a variable (see Appendix IV - Variables) or macro name. For each cs:key element, the sort direction can
|
||||
* be set to either “ascending” (default) or “descending” with the sort attribute.
|
||||
*
|
||||
* TODO: The attributes names-min, names-use-first, and names-use-last may be used to override the values of the
|
||||
* corresponding et-al-min/et-al-subsequent-min, et-al-use-first/et-al-subsequent-use-first and et-al-use-last
|
||||
* attributes, and affect all names generated via macros called by cs:key.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
$name = $attribute->getName();
|
||||
if ($name === "variable") {
|
||||
$this->variable = (string) $attribute;
|
||||
}
|
||||
if ($name === "sort") {
|
||||
$this->sort = (string) $attribute;
|
||||
}
|
||||
if ($name === "macro") {
|
||||
$this->variable = "macro";
|
||||
$this->macro = (string) $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVariable()
|
||||
{
|
||||
return $this->variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string (ascending|descending)
|
||||
*/
|
||||
public function getSort()
|
||||
{
|
||||
return $this->sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMacro()
|
||||
{
|
||||
return $this->macro;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNameVariable()
|
||||
{
|
||||
return Variables::isNameVariable($this->variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNumberVariable()
|
||||
{
|
||||
return Variables::isNumberVariable($this->variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDateVariable()
|
||||
{
|
||||
return Variables::isDateVariable($this->variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isMacro()
|
||||
{
|
||||
return $this->variable === "macro" && !empty(CiteProc::getContext()->getMacro($this->macro));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $rangePart
|
||||
*/
|
||||
public function setRangePart($rangePart)
|
||||
{
|
||||
$this->rangePart = $rangePart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getRangePart()
|
||||
{
|
||||
return $this->rangePart;
|
||||
}
|
||||
}
|
||||
Vendored
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Sort;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Data\DataList;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Util\DateHelper;
|
||||
use Seboettg\CiteProc\Util\Variables;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Sort
|
||||
*
|
||||
* cs:citation and cs:bibliography may include a cs:sort child element before the cs:layout element to specify the
|
||||
* sorting order of respectively cites within citations, and bibliographic entries within the bibliography.
|
||||
*
|
||||
* The cs:sort element must contain one or more cs:key child elements. The sort key, set as an attribute on cs:key, must
|
||||
* be a variable (see Appendix IV - Variables) or macro name. For each cs:key element, the sort direction can be set to
|
||||
* either “ascending” (default) or “descending” with the sort attribute.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Sort
|
||||
{
|
||||
/**
|
||||
* ordered list contains sorting keys
|
||||
*
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $sortingKeys;
|
||||
|
||||
/**
|
||||
* @var SimpleXMLElement $node
|
||||
*/
|
||||
public function __construct(SimpleXMLElement $node)
|
||||
{
|
||||
$this->sortingKeys = new ArrayList();
|
||||
/** @var SimpleXMLElement $child */
|
||||
foreach ($node->children() as $child) {
|
||||
if ("key" === $child->getName()) {
|
||||
$this->sortingKeys->append(new Key($child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function in order to sort a set of csl items by one or multiple sort keys.
|
||||
* Sort keys are evaluated in sequence. A primary sort is performed on all items using the first sort key.
|
||||
* A secondary sort, using the second sort key, is applied to items sharing the first sort key value. A tertiary
|
||||
* sort, using the third sort key, is applied to items sharing the first and second sort key values. Sorting
|
||||
* continues until either the order of all items is fixed, or until the sort keys are exhausted. Items with an
|
||||
* empty sort key value are placed at the end of the sort, both for ascending and descending sorts.
|
||||
*
|
||||
* @param DataList|array $data reference
|
||||
*/
|
||||
public function sort(&$data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$data = new DataList(...$data);
|
||||
}
|
||||
$dataToSort = $data->toArray();
|
||||
try {
|
||||
$data->replace($this->performSort(0, $dataToSort));
|
||||
} catch (CiteProcException $e) {
|
||||
//nothing to do, because $data is passed by referenced
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function in order to sort a set of csl items by one or multiple sort keys.
|
||||
* All items will be distributed by the value (defined in respective sort key) in an associative array (grouped).
|
||||
* Afterwards the array will be sorted by the array key. If a further sort key exist, each of these groups will be
|
||||
* sorted by a recursive function call. Finally the array will be flatted.
|
||||
*
|
||||
* @param $keyNumber
|
||||
* @param array $dataToSort
|
||||
* @return array
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private function performSort($keyNumber, $dataToSort)
|
||||
{
|
||||
if (count($dataToSort) < 2) {
|
||||
return $dataToSort;
|
||||
}
|
||||
|
||||
/** @var Key $key */
|
||||
$key = $this->sortingKeys->get($keyNumber);
|
||||
$variable = $key->getVariable();
|
||||
$groupedItems = [];
|
||||
|
||||
if ($key->isDateVariable()) {
|
||||
if (DateHelper::hasDateRanges($dataToSort, $variable, "all")) {
|
||||
$newKey = clone $key;
|
||||
$newKey->setRangePart(2);
|
||||
$key->setRangePart(1);
|
||||
$this->sortingKeys->append($newKey);
|
||||
}
|
||||
}
|
||||
|
||||
//grouping by value
|
||||
foreach ($dataToSort as $citationNumber => $dataItem) {
|
||||
if ($key->isNameVariable()) {
|
||||
$sortKey = Variables::nameHash($dataItem, $variable);
|
||||
} elseif ($key->isNumberVariable()) {
|
||||
$sortKey = $dataItem->{$variable};
|
||||
} elseif ($key->isDateVariable()) {
|
||||
$sortKey = DateHelper::getSortKeyDate($dataItem, $key);
|
||||
} elseif ($key->isMacro()) {
|
||||
$sortKey = mb_strtolower(strip_tags(CiteProc::getContext()->getMacro(
|
||||
$key->getMacro()
|
||||
)->render($dataItem, $citationNumber)));
|
||||
} elseif ($variable === "citation-number") {
|
||||
$sortKey = $citationNumber + 1;
|
||||
} else {
|
||||
$sortKey = mb_strtolower(strip_tags($dataItem->{$variable}));
|
||||
}
|
||||
$groupedItems[$sortKey][] = $dataItem;
|
||||
}
|
||||
|
||||
if ($this->sortingKeys->count() > ++$keyNumber) {
|
||||
foreach ($groupedItems as $group => &$array) {
|
||||
if (count($array) > 1) {
|
||||
$array = $this->performSort($keyNumber, $array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sorting by array keys
|
||||
if ($key->getSort() === "ascending") {
|
||||
ksort($groupedItems); //ascending
|
||||
} else {
|
||||
krsort($groupedItems); //reverse
|
||||
}
|
||||
|
||||
//the flattened array is the result
|
||||
$sortedDataGroups = array_values($groupedItems);
|
||||
return $this->flatten($sortedDataGroups);
|
||||
}
|
||||
|
||||
public function flatten($array)
|
||||
{
|
||||
$returnArray = [];
|
||||
array_walk_recursive($array, function ($a) use (&$returnArray) {
|
||||
$returnArray[] = $a;
|
||||
});
|
||||
return $returnArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getSortingKeys()
|
||||
{
|
||||
return $this->sortingKeys;
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style\Sort;
|
||||
|
||||
interface SortKey
|
||||
{
|
||||
public function getVariable();
|
||||
|
||||
public function getSort();
|
||||
|
||||
public function isNameVariable();
|
||||
|
||||
public function isNumberVariable();
|
||||
|
||||
public function isDateVariable();
|
||||
|
||||
public function isMacro();
|
||||
}
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Style;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\Layout;
|
||||
use Seboettg\CiteProc\Root\Root;
|
||||
use Seboettg\CiteProc\Style\Sort\Sort;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class StyleElement
|
||||
*
|
||||
* StyleElement is an abstract class which must be extended by Citation and Bibliography class. The constructor
|
||||
* of StyleElement class parses the cs:layout element (necessary for cs:citation and cs:bibliography) and the optional
|
||||
* cs:sort element.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Style
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
abstract class StyleElement
|
||||
{
|
||||
|
||||
use InheritableNameAttributesTrait;
|
||||
/**
|
||||
* @var Layout
|
||||
*/
|
||||
protected $layout;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $doNotSort;
|
||||
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* Parses the configuration.
|
||||
*
|
||||
* @param SimpleXMLElement $node
|
||||
* @param Root $parent
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
protected function __construct(SimpleXMLElement $node, $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
// init child elements
|
||||
/** @var SimpleXMLElement $child */
|
||||
foreach ($node->children() as $child) {
|
||||
switch ($child->getName()) {
|
||||
/* The cs:layout rendering element is a required child element of cs:citation and cs:bibliography. It
|
||||
* must contain one or more of the other rendering elements described below, and may carry affixes and
|
||||
* formatting attributes.
|
||||
*/
|
||||
case 'layout':
|
||||
$this->layout = new Layout($child, $this);
|
||||
break;
|
||||
|
||||
/* cs:citation and cs:bibliography may include a cs:sort child element before the cs:layout element to
|
||||
* specify the sorting order of respectively cites within citations, and bibliographic entries within
|
||||
* the bibliography. In the absence of cs:sort, cites and bibliographic entries appear in the order in
|
||||
* which they are cited.
|
||||
*/
|
||||
case 'sort':
|
||||
$sorting = new Sort($child);
|
||||
CiteProc::getContext()->setSorting($sorting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Root
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc;
|
||||
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
|
||||
/**
|
||||
* Class StyleSheet
|
||||
*
|
||||
* Helper class for loading CSL styles and CSL locales
|
||||
*
|
||||
* @package Seboettg\CiteProc
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class StyleSheet
|
||||
{
|
||||
|
||||
/**
|
||||
* Loads xml formatted CSL stylesheet of a given stylesheet name, e.g. "american-physiological-society" for
|
||||
* apa style.
|
||||
*
|
||||
* See in styles folder (which is included as git submodule) for all available style sheets
|
||||
*
|
||||
* @param string $styleName e.g. "american-physiological-society" for apa
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function loadStyleSheet(string $styleName): string
|
||||
{
|
||||
$stylesPath = self::vendorPath() . "/citation-style-language/styles";
|
||||
return self::readFileContentsOrThrowException("$stylesPath/$styleName.csl");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads xml formatted locales of given language key
|
||||
*
|
||||
* @param string $langKey e.g. "en-US", or "de-CH"
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function loadLocales(string $langKey): string
|
||||
{
|
||||
$localesPath = self::vendorPath()."/citation-style-language/locales";
|
||||
$localeFile = "$localesPath/locales-${langKey}.xml";
|
||||
if (file_exists($localeFile)) {
|
||||
return self::readFileContentsOrThrowException($localeFile);
|
||||
} else {
|
||||
$metadata = self::loadLocalesMetadata();
|
||||
if (!empty($metadata->{'primary-dialects'}->{$langKey})) {
|
||||
return self::readFileContentsOrThrowException(
|
||||
sprintf("%s/locales-%s.xml", $localesPath, $metadata->{'primary-dialects'}->{$langKey})
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new CiteProcException("No Locale file found for $langKey");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private static function readFileContentsOrThrowException($path): string
|
||||
{
|
||||
$fileContent = file_get_contents($path);
|
||||
if (false === $fileContent) {
|
||||
throw new CiteProcException("Couldn't read $path");
|
||||
}
|
||||
return $fileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function loadLocalesMetadata()
|
||||
{
|
||||
$localesMetadataPath = self::vendorPath() . "/citation-style-language/locales/locales.json";
|
||||
return json_decode(self::readFileContentsOrThrowException($localesMetadataPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
private static function vendorPath()
|
||||
{
|
||||
include_once realpath(__DIR__ . '/../') . '/vendorPath.php';
|
||||
if (!($vendorPath = vendorPath())) {
|
||||
throw new CiteProcException('vendor path not found. Use composer to initialize your project');
|
||||
}
|
||||
return $vendorPath;
|
||||
}
|
||||
}
|
||||
Vendored
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait AffixesTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait AffixesTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $prefix = "";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $suffix = "";
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $quote = false;
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
protected function initAffixesAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
$name = (string) $attribute->getName();
|
||||
$value = (string) $attribute;
|
||||
|
||||
switch ($name) {
|
||||
case 'prefix':
|
||||
$this->prefix = $value;
|
||||
break;
|
||||
case 'suffix':
|
||||
$this->suffix = $value;
|
||||
break;
|
||||
case 'quote':
|
||||
$this->quote = (bool) $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @return string
|
||||
*/
|
||||
protected function addAffixes($text)
|
||||
{
|
||||
$prefix = $this->prefix;
|
||||
$suffix = $this->suffix;
|
||||
|
||||
if (!empty($suffix)) { // guard against repeated suffixes...
|
||||
$no_tags = strip_tags($text);
|
||||
if (strlen($no_tags) && ($no_tags[(strlen($no_tags) - 1)] == $suffix[0])) {
|
||||
$suffix = substr($suffix, 1);
|
||||
}
|
||||
|
||||
// punctuation in quote?
|
||||
$piq = CiteProc::getContext()
|
||||
->getLocale()
|
||||
->filter('options', 'punctuation-in-quote');
|
||||
$punctuationInQuote = is_array($piq) ? current($piq) : $piq;
|
||||
|
||||
if ($punctuationInQuote && in_array($suffix, [',', ';', '.'])) {
|
||||
$closeQuote = CiteProc::getContext()->getLocale()->filter("terms", "close-quote")->single;
|
||||
$lastChar = mb_substr($text, -1, 1);
|
||||
if ($closeQuote === $lastChar) { // last char is closing quote?
|
||||
$text = mb_substr($text, 0, mb_strlen($text) - 1); //set suffix before
|
||||
return $prefix . $text . $suffix . $lastChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $prefix . $text . $suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function renderPrefix()
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function renderSuffix()
|
||||
{
|
||||
return $this->suffix;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
/**
|
||||
* Trait ConsecutivePunctuationCharacterTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait ConsecutivePunctuationCharacterTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $childrenPrefixes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $childrenSuffixes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $childrenDelimiter = [];
|
||||
|
||||
/**
|
||||
* @param $punctuationSign
|
||||
* @param $subject
|
||||
* @return string
|
||||
*/
|
||||
public function removeConsecutivePunctuation($punctuationSign, $subject): string
|
||||
{
|
||||
if (empty($punctuationSign) || preg_match("/^\s+$/", $punctuationSign)) {
|
||||
return $subject;
|
||||
}
|
||||
$pattern = '/'.preg_quote(trim($punctuationSign), '/').'{2,}/';
|
||||
if (preg_match($pattern, $subject)) {
|
||||
return preg_replace($pattern, $punctuationSign, $subject);
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $child
|
||||
*/
|
||||
protected function getChildrenAffixesAndDelimiter($child)
|
||||
{
|
||||
if (method_exists($child, "renderPrefix")) {
|
||||
if (!empty($child->renderPrefix()) && !in_array($child->renderPrefix(), $this->childrenPrefixes)) {
|
||||
$this->childrenPrefixes[] = $child->renderPrefix();
|
||||
}
|
||||
}
|
||||
if (method_exists($child, "renderSuffix")) {
|
||||
if (!empty($child->renderSuffix()) && !in_array($child->renderSuffix(), $this->childrenSuffixes)) {
|
||||
$this->childrenSuffixes[] = $child->renderSuffix();
|
||||
}
|
||||
}
|
||||
if (method_exists($child, "getDelimiter")) {
|
||||
if (!empty($child->getDelimiter()) && !in_array($child->getDelimiter(), $this->childrenDelimiter)) {
|
||||
$this->childrenDelimiter[] = $child->getDelimiter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function removeConsecutiveChars($string)
|
||||
{
|
||||
foreach ($this->childrenPrefixes as $prefix) {
|
||||
$string = $this->removeConsecutivePunctuation($prefix, $string);
|
||||
}
|
||||
foreach ($this->childrenSuffixes as $suffix) {
|
||||
$string = $this->removeConsecutivePunctuation($suffix, $string);
|
||||
}
|
||||
foreach ($this->childrenDelimiter as $delimiter) {
|
||||
$string = $this->removeConsecutivePunctuation($delimiter, $string);
|
||||
}
|
||||
|
||||
$string = preg_replace("/\s{2,}/", " ", $string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles\Css;
|
||||
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class CssRule
|
||||
* @package Seboettg\CiteProc\Styles\Css
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CssRule
|
||||
{
|
||||
const SELECTOR_TYPE_ID = "#";
|
||||
|
||||
const SELECTOR_TYPE_CLASS = ".";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $selectorType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $selector;
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $directives;
|
||||
|
||||
/**
|
||||
* CssRule constructor.
|
||||
* @param string $selector
|
||||
* @param string $selectorType
|
||||
*/
|
||||
public function __construct($selector, $selectorType = self::SELECTOR_TYPE_CLASS)
|
||||
{
|
||||
$this->selector = $selector;
|
||||
$this->selectorType = $selectorType;
|
||||
$this->directives = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $property
|
||||
* @param string $value
|
||||
*/
|
||||
public function addDirective($property, $value)
|
||||
{
|
||||
$this->directives->append("$property: $value;");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$directives = "\t".implode("\n\t", $this->directives->toArray());
|
||||
return $this->selectorType.$this->selector." {\n".$directives."\n}\n";
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles\Css;
|
||||
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class CssRules
|
||||
* @package Seboettg\CiteProc\Styles\Css
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CssRules extends ArrayList
|
||||
{
|
||||
/**
|
||||
* @param $rule
|
||||
* @return CssRule
|
||||
*/
|
||||
public function getRule($rule)
|
||||
{
|
||||
if (!$this->hasKey($rule)) {
|
||||
$this->set($rule, new CssRule(substr($rule, 1), substr($rule, 0, 1)));
|
||||
}
|
||||
return $this->get($rule);
|
||||
}
|
||||
}
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles\Css;
|
||||
|
||||
use Seboettg\CiteProc\Style\Options\BibliographyOptions;
|
||||
|
||||
/**
|
||||
* Class CssStyle
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class CssStyle
|
||||
{
|
||||
/**
|
||||
* @var BibliographyOptions
|
||||
*/
|
||||
private $bibliographyOptions;
|
||||
|
||||
/**
|
||||
* @var CssRules
|
||||
*/
|
||||
private $cssRules = null;
|
||||
|
||||
/**
|
||||
* CssStyle constructor.
|
||||
* @param BibliographyOptions $bibliographyOptions
|
||||
*/
|
||||
public function __construct(BibliographyOptions $bibliographyOptions)
|
||||
{
|
||||
$this->bibliographyOptions = $bibliographyOptions;
|
||||
$this->cssRules = new CssRules();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* renders CSS output
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return implode("\n", array_filter($this->cssRules->toArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize CSS rules
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
$lineSpacing = $this->bibliographyOptions->getLineSpacing();
|
||||
$entrySpacing = $this->bibliographyOptions->getEntrySpacing();
|
||||
$hangingIndent = $this->bibliographyOptions->getHangingIndent();
|
||||
|
||||
if ($lineSpacing || $entrySpacing || $hangingIndent) {
|
||||
$rule = $this->cssRules->getRule(".csl-entry");
|
||||
if (!empty($lineSpacing)) {
|
||||
$rule->addDirective("line-height", intval($lineSpacing) . "em");
|
||||
}
|
||||
|
||||
if (!empty($entrySpacing)) {
|
||||
$rule->addDirective("margin-bottom", intval($entrySpacing) . "em");
|
||||
}
|
||||
|
||||
if (!empty($hangingIndent)) {
|
||||
$rule->addDirective("padding-left", "2em");
|
||||
$rule->addDirective("text-indent", "-2em");
|
||||
}
|
||||
}
|
||||
|
||||
if ("flush" === $this->bibliographyOptions->getSecondFieldAlign()) {
|
||||
$rule = $this->cssRules->getRule(".csl-left-margin");
|
||||
$rule->addDirective("display", "block");
|
||||
$rule->addDirective("float", "left");
|
||||
|
||||
$rule = $this->cssRules->getRule(".csl-right-inline");
|
||||
$rule->addDirective("margin-left", "35px");
|
||||
}
|
||||
}
|
||||
}
|
||||
plugins/generic/citationStyleLanguage/lib/vendor/seboettg/citeproc-php/src/Styles/DelimiterTrait.php
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait DelimiterTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait DelimiterTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
protected function initDelimiterAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
/** @var string $name */
|
||||
$name = (string) $attribute->getName();
|
||||
$value = (string) $attribute;
|
||||
|
||||
switch ($name) {
|
||||
case 'delimiter':
|
||||
$this->delimiter = $value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait DisplayTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait DisplayTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowedValues = [
|
||||
"block",
|
||||
"left-margin",
|
||||
"right-inline",
|
||||
"indent"
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $display;
|
||||
|
||||
/**
|
||||
* @param $node
|
||||
*/
|
||||
public function initDisplayAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
switch ($attribute->getName()) {
|
||||
case 'display':
|
||||
$this->display = (string) $attribute;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @return string
|
||||
*/
|
||||
public function wrapDisplayBlock($text)
|
||||
{
|
||||
if (!in_array($this->display, self::$allowedValues)) {
|
||||
return $text;
|
||||
}
|
||||
$divStyle = "class=\"csl-".$this->display."\"";
|
||||
return "<div $divStyle>$text</div>";
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use Seboettg\Collection\ArrayList;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait FormattingTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait FormattingTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $formattingAttributes = [
|
||||
'font-style',
|
||||
'font-family',
|
||||
'font-weight',
|
||||
'font-variant',
|
||||
'text-decoration',
|
||||
'vertical-align'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
private $formattingOptions;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $stripPeriods = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $format;
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
*/
|
||||
protected function initFormattingAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
$this->formattingOptions = new ArrayList();
|
||||
|
||||
/** @var SimpleXMLElement $attribute */
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
|
||||
/** @var string $name */
|
||||
$name = (string) $attribute->getName();
|
||||
$value = (string) $attribute;
|
||||
|
||||
if (in_array($name, self::$formattingAttributes)) {
|
||||
$this->formattingOptions->add($name, $value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function format($text)
|
||||
{
|
||||
if (empty($text)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
if ($this->formattingOptions->count() > 0) {
|
||||
$format = "";
|
||||
foreach ($this->formattingOptions as $option => $optionValue) {
|
||||
if ($optionValue === "italic") {
|
||||
$text = "<i>$text</i>";
|
||||
} elseif ($optionValue === "bold") {
|
||||
$text = "<b>$text</b>";
|
||||
} elseif ($optionValue === "normal") {
|
||||
$text = "$text";
|
||||
} elseif ($option === "vertical-align") {
|
||||
if ($optionValue === "sub") {
|
||||
$text = "<sub>$text</sub>";
|
||||
} elseif ($optionValue === "sup") {
|
||||
$text = "<sup>$text</sup>";
|
||||
}
|
||||
} elseif ($option === "text-decoration" && $optionValue === "none") {
|
||||
$format .= "";
|
||||
} else {
|
||||
$format .= "$option: $optionValue;";
|
||||
}
|
||||
}
|
||||
if (!empty($format)) {
|
||||
$text = '<span style="' . $format . '">' . $text . '</span>';
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait QuotesTrait
|
||||
*
|
||||
* The quotes attribute may set on cs:text. When set to “true” (“false” is default), the rendered text is wrapped in
|
||||
* quotes (the quotation marks used are terms). The localized punctuation-in-quote option controls whether an adjoining
|
||||
* comma or period appears outside (default) or inside the closing quotation mark.
|
||||
*
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait QuotesTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $quotes = false;
|
||||
|
||||
public function initQuotesAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
if (isset($node['quotes']) && "true" === (string) $node['quotes']) {
|
||||
$this->quotes = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public function addSurroundingQuotes($text)
|
||||
{
|
||||
if ($this->quotes) {
|
||||
$openQuote = CiteProc::getContext()->getLocale()->filter("terms", "open-quote")->single;
|
||||
$closeQuote = CiteProc::getContext()->getLocale()->filter("terms", "close-quote")->single;
|
||||
$punctuationInQuotes = CiteProc::getContext()->getLocale()->filter("options", "punctuation-in-quote");
|
||||
$text = $this->replaceOuterQuotes($text, $openQuote, $closeQuote);
|
||||
if (null !== $punctuationInQuotes || $punctuationInQuotes === false) {
|
||||
if (preg_match("/^(.+)([\.,;]+)$/", $text, $match)) {
|
||||
$punctuation = substr($match[2], -1);
|
||||
if ($this->suffix !== $punctuation) {
|
||||
$text = $match[1] . substr($match[2], 0, strlen($match[2]) - 1);
|
||||
return $openQuote . $text . $closeQuote . $punctuation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $openQuote . $text . $closeQuote;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @param $outerOpenQuote
|
||||
* @param $outerCloseQuote
|
||||
* @return string
|
||||
*/
|
||||
private function replaceOuterQuotes($text, $outerOpenQuote, $outerCloseQuote)
|
||||
{
|
||||
$innerOpenQuote = CiteProc::getContext()
|
||||
->getLocale()
|
||||
->filter("terms", "open-inner-quote")
|
||||
->single;
|
||||
$innerCloseQuote = CiteProc::getContext()
|
||||
->getLocale()
|
||||
->filter("terms", "close-inner-quote")
|
||||
->single;
|
||||
$text = StringHelper::replaceOuterQuotes(
|
||||
$text,
|
||||
"\"",
|
||||
"\"",
|
||||
$innerOpenQuote,
|
||||
$innerCloseQuote
|
||||
);
|
||||
$text = StringHelper::replaceOuterQuotes(
|
||||
$text,
|
||||
$outerOpenQuote,
|
||||
$outerCloseQuote,
|
||||
$innerOpenQuote,
|
||||
$innerCloseQuote
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
/**
|
||||
* Trait RangeDelimiterTrait
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait RangeDelimiterTrait
|
||||
{
|
||||
}
|
||||
Vendored
+95
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Styles;
|
||||
|
||||
use Seboettg\CiteProc\Util\StringHelper;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Trait TextCase
|
||||
*
|
||||
* @package Seboettg\CiteProc\Styles
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
trait TextCaseTrait
|
||||
{
|
||||
|
||||
private $textCase;
|
||||
|
||||
protected function initTextCaseAttributes(SimpleXMLElement $node)
|
||||
{
|
||||
foreach ($node->attributes() as $attribute) {
|
||||
$name = $attribute->getName();
|
||||
$value = (string) $attribute;
|
||||
|
||||
switch ($name) {
|
||||
case 'text-case':
|
||||
$this->textCase = $value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @param string $lang
|
||||
* @return string
|
||||
*/
|
||||
public function applyTextCase($text, $lang = "en")
|
||||
{
|
||||
|
||||
switch ($this->textCase) {
|
||||
case 'uppercase':
|
||||
$text = $this->keepNoCase(mb_strtoupper($text), $text);
|
||||
break;
|
||||
case 'lowercase':
|
||||
$text = $this->keepNoCase(mb_strtolower($text), $text);
|
||||
break;
|
||||
case 'sentence':
|
||||
if (StringHelper::checkUpperCaseString($text)) {
|
||||
$text = mb_strtolower($text);
|
||||
return StringHelper::mb_ucfirst($text);
|
||||
} else {
|
||||
return StringHelper::mb_ucfirst($text);
|
||||
}
|
||||
break;
|
||||
case 'capitalize-all':
|
||||
$text = $this->keepNoCase(StringHelper::capitalizeAll($text), $text);
|
||||
break;
|
||||
case 'title':
|
||||
if ($lang === "en") {
|
||||
$text = $this->keepNoCase(StringHelper::capitalizeForTitle($text), $text);
|
||||
}
|
||||
break;
|
||||
case 'capitalize-first':
|
||||
$text = $this->keepNoCase(StringHelper::mb_ucfirst($text), $text);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $render
|
||||
* @param string $original
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
private function keepNoCase($render, $original)
|
||||
{
|
||||
if (preg_match('/<span class=\"nocase\">(\p{L}+)<\/span>/i', $original, $match)) {
|
||||
return preg_replace('/(<span class=\"nocase\">\p{L}+<\/span>)/i', $match[1], $render);
|
||||
}
|
||||
return $render;
|
||||
}
|
||||
}
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php: Locator.php
|
||||
* User: Sebastian Böttger <seboettg@gmail.com>
|
||||
* created at 08.04.20, 15:21
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Terms;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
class Locator extends Enum
|
||||
{
|
||||
const BOOK = "book";
|
||||
const CHAPTER = "chapter";
|
||||
const COLUMN = "column";
|
||||
const FIGURE = "figure";
|
||||
const FOLIO = "folio";
|
||||
const ISSUE = "issue";
|
||||
const LINE = "line";
|
||||
const NOTE = "note";
|
||||
const OPUS = "opus";
|
||||
const PAGE = "page";
|
||||
const PARAGRAPH = "paragraph";
|
||||
const PART = "part";
|
||||
const SECTION = "section";
|
||||
const SUB_VERBO = "sub-verbo";
|
||||
const VERSE = "verse";
|
||||
const VOLUME = "volume";
|
||||
|
||||
private const LABEL_TO_VARIABLE_MAP = [
|
||||
"chapter" => "chapter-number",
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string|Locator $locatorTerm
|
||||
* @return string
|
||||
*/
|
||||
public static function mapLocatorLabelToRenderVariable($locatorTerm)
|
||||
{
|
||||
if ($locatorTerm instanceof Locator) {
|
||||
$locatorTerm = (string)$locatorTerm;
|
||||
}
|
||||
return
|
||||
array_key_exists($locatorTerm, self::LABEL_TO_VARIABLE_MAP) ?
|
||||
self::LABEL_TO_VARIABLE_MAP[$locatorTerm] : $locatorTerm;
|
||||
}
|
||||
}
|
||||
Vendored
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use stdClass;
|
||||
|
||||
class CiteProcHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Applies additional functions for markup extension
|
||||
*
|
||||
* @param stdClass $dataItem the actual item
|
||||
* @param string $valueToRender value the has to apply on
|
||||
* @param string $renderedText actual by citeproc rendered text
|
||||
* @return string
|
||||
*/
|
||||
public static function applyAdditionMarkupFunction($dataItem, $valueToRender, $renderedText)
|
||||
{
|
||||
$markupExtension = CiteProc::getContext()->getMarkupExtension();
|
||||
if (array_key_exists($valueToRender, $markupExtension)) {
|
||||
if (is_array($markupExtension[$valueToRender]) && array_key_exists('function', $markupExtension[$valueToRender])) {
|
||||
$function = $markupExtension[$valueToRender]['function'];
|
||||
} else {
|
||||
$function = $markupExtension[$valueToRender];
|
||||
}
|
||||
if (is_callable($function)) {
|
||||
$renderedText = $function($dataItem, $renderedText);
|
||||
}
|
||||
} elseif (array_key_exists($mode = CiteProc::getContext()->getMode(), $markupExtension)) {
|
||||
if (array_key_exists($valueToRender, $markupExtension[$mode])) {
|
||||
if (is_array($markupExtension[$mode][$valueToRender]) && array_key_exists('function', $markupExtension[$mode][$valueToRender])) {
|
||||
$function = CiteProc::getContext()->getMarkupExtension()[$mode][$valueToRender]['function'];
|
||||
} else {
|
||||
$function = CiteProc::getContext()->getMarkupExtension()[$mode][$valueToRender];
|
||||
}
|
||||
if (is_callable($function)) {
|
||||
$renderedText = $function($dataItem, $renderedText);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $renderedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
public static function cloneArray(array $array)
|
||||
{
|
||||
$newArray = [];
|
||||
foreach ($array as $key => $value) {
|
||||
$newArray[$key] = clone $value;
|
||||
}
|
||||
return $newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $dataItem the actual item
|
||||
* @param string $valueToRender value the has to apply on
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUsingAffixesByMarkupExtentsion($dataItem, $valueToRender)
|
||||
{
|
||||
$markupExtension = CiteProc::getContext()->getMarkupExtension();
|
||||
if (array_key_exists($valueToRender, $markupExtension)) {
|
||||
if (is_array($markupExtension[$valueToRender]) && array_key_exists('affixes', $markupExtension[$valueToRender])) {
|
||||
return $markupExtension[$valueToRender]['affixes'];
|
||||
}
|
||||
} elseif (array_key_exists($mode = CiteProc::getContext()->getMode(), $markupExtension)) {
|
||||
if (array_key_exists($valueToRender, $markupExtension[$mode])) {
|
||||
if (is_array($markupExtension[$mode][$valueToRender]) && array_key_exists('affixes', $markupExtension[$mode][$valueToRender])) {
|
||||
return CiteProc::getContext()->getMarkupExtension()[$mode][$valueToRender]['affixes'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Style\Sort\Key;
|
||||
|
||||
/**
|
||||
* Class Date
|
||||
*
|
||||
* Just a helper class for date issues
|
||||
*
|
||||
* @package Seboettg\CiteProc\Util
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class DateHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* dates: Date variables called via the variable attribute are returned in the YYYYMMDD format, with zeros
|
||||
* substituted for any missing date-parts (e.g. 20001200 for December 2000). As a result, less specific dates
|
||||
* precede more specific dates in ascending sorts, e.g. “2000, May 2000, May 1st 2000”. Negative years are sorted
|
||||
* inversely, e.g. “100BC, 50BC, 50AD, 100AD”. Seasons are ignored for sorting, as the chronological order of the
|
||||
* seasons differs between the northern and southern hemispheres.
|
||||
*
|
||||
* @param array $dateParts
|
||||
* @return string
|
||||
*/
|
||||
public static function serializeDate($dateParts)
|
||||
{
|
||||
$year = isset($dateParts[0]) ? $dateParts[0] : "0000";
|
||||
$month = isset($dateParts[1]) ? $dateParts[1] : "00";
|
||||
$day = isset($dateParts[2]) ? $dateParts[2] : "00";
|
||||
|
||||
return sprintf("%04d%02d%02d", $year, $month, $day);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @return array
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function parseDateParts($date)
|
||||
{
|
||||
if (!isset($date->{'raw'})) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
$dateTime = new DateTime($date->{'raw'});
|
||||
$arr = [[$dateTime->format("Y"), $dateTime->format("m"), $dateTime->format("d")]];
|
||||
} catch (Exception $e) {
|
||||
throw new CiteProcException("Could not parse date \"".$date->{'raw'}."\".", 0, $e);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates sort key for variables containing date and date ranges
|
||||
* @param array $dataItem
|
||||
* @param Key $key
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function getSortKeyDate($dataItem, $key)
|
||||
{
|
||||
$variable = $variable = $key->getVariable();
|
||||
$part = $key->getRangePart();
|
||||
if (count($dataItem->{$variable}->{'date-parts'}) > 1) {
|
||||
//Date range
|
||||
$datePart = $dataItem->{$variable}->{'date-parts'}[$part];
|
||||
$sortKey = self::serializeDate($datePart);
|
||||
if ($key->getSort() === "descending" && $part === 0 ||
|
||||
$key->getSort() === "ascending" && $part === 1) {
|
||||
$sortKey .= "-";
|
||||
}
|
||||
} else {
|
||||
if (!isset($dataItem->{$variable}->{'date-parts'})) {
|
||||
$dateParts = self::parseDateParts($dataItem->{$variable});
|
||||
} else {
|
||||
$dateParts = $dataItem->{$variable}->{'date-parts'}[0];
|
||||
}
|
||||
$sortKey = self::serializeDate($dateParts);
|
||||
}
|
||||
return $sortKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param string $variable name of the date variable
|
||||
* @param string $match ("all"|"any") default "all"
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasDateRanges($items, $variable, $match = "all")
|
||||
{
|
||||
$ret = true;
|
||||
foreach ($items as $item) {
|
||||
$dateParts = $item->{$variable}->{"date-parts"};
|
||||
if ($match === "all" && count($dateParts) !== 2) {
|
||||
return false;
|
||||
} elseif ($match === "any" && count($dateParts) === 2) {
|
||||
return true;
|
||||
} else {
|
||||
$ret = ($match === "all") ? $ret&true : $ret|true;
|
||||
}
|
||||
}
|
||||
return (bool) $ret;
|
||||
}
|
||||
}
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Class Factory
|
||||
* @package Seboettg\CiteProc\Util
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
const CITE_PROC_NODE_NAMESPACE = "Seboettg\\CiteProc\\Rendering";
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $nodes = [
|
||||
|
||||
'layout' => "\\Layout",
|
||||
'text' => "\\Text",
|
||||
"macro" => "\\Macro",
|
||||
"number" => "\\Number",
|
||||
"label" => "\\Label",
|
||||
"group" => "\\Group",
|
||||
"choose" => "\\Choose\\Choose",
|
||||
"if" => "\\Choose\\ChooseIf",
|
||||
"else-if" => "\\Choose\\ChooseElseIf",
|
||||
"else" => "\\Choose\\ChooseElse",
|
||||
'date' => "\\Date\\Date",
|
||||
"date-part" => "\\Date\\DatePart",
|
||||
"names" => "\\Name\\Names",
|
||||
"name" => "\\Name\\Name",
|
||||
"name-part" => "\\Name\\NamePart",
|
||||
"substitute" => "\\Name\\Substitute",
|
||||
"et-al" => "\\Name\\EtAl"
|
||||
];
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $node
|
||||
* @param mixed $param
|
||||
* @return mixed
|
||||
* @throws InvalidStylesheetException
|
||||
*/
|
||||
public static function create($node, $param = null)
|
||||
{
|
||||
$nodeClass = self::CITE_PROC_NODE_NAMESPACE.self::$nodes[$node->getName()];
|
||||
if (!class_exists($nodeClass)) {
|
||||
throw new InvalidStylesheetException("For node {$node->getName()} ".
|
||||
"does not exist any counterpart class \"".$nodeClass.
|
||||
"\". The given stylesheet seems to be invalid.");
|
||||
}
|
||||
if ($param != null) {
|
||||
return new $nodeClass($node, $param);
|
||||
}
|
||||
return new $nodeClass($node);
|
||||
}
|
||||
}
|
||||
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class NameHelper
|
||||
* @package Seboettg\CiteProc\Util
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class NameHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* @param stdClass $precedingItem
|
||||
* @param array $currentAuthor
|
||||
* @return bool
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function identicalAuthors($precedingItem, $currentAuthor)
|
||||
{
|
||||
if (!property_exists($precedingItem, "author")) {
|
||||
throw new CiteProcException("No author to present");
|
||||
}
|
||||
|
||||
if (count($precedingItem->author) !== count($currentAuthor)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($currentAuthor as $current) {
|
||||
if (self::precedingHasAuthor($precedingItem, $current)) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $preceding
|
||||
* @param stdClass $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function precedingHasAuthor($preceding, $name)
|
||||
{
|
||||
foreach ($preceding->author as $author) {
|
||||
if ($author->family === $name->family && $author->given === $name->given) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* removes the field $particle from $data and appends its content to the $namePart field of $data
|
||||
* @param stdClass $data
|
||||
* @param string $namePart
|
||||
* @param string $particle
|
||||
*/
|
||||
public static function appendParticleTo(&$data, $namePart, $particle)
|
||||
{
|
||||
if (isset($data->{$particle}) && isset($data->{$namePart})) {
|
||||
$data->{$namePart} = $data->{$namePart}." ".$data->{$particle}; // append $particle to $namePart
|
||||
unset($data->{$particle}); //remove particle from $data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the field $particle from $data and prepends its content to the $namePart field of $data
|
||||
* @param stdClass $data
|
||||
* @param string $namePart ("given"|"family")
|
||||
* @param string $particle
|
||||
*/
|
||||
public static function prependParticleTo(&$data, $namePart, $particle)
|
||||
{
|
||||
if (isset($data->{$particle}) && isset($data->{$namePart})) {
|
||||
$data->{$namePart} = $data->{$particle}." ".$data->{$namePart}; //prepend $particle to $namePart
|
||||
unset($data->{$particle}); //remove particle from $data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $persons1
|
||||
* @param array $persons2
|
||||
* @return bool
|
||||
*/
|
||||
public static function sameNames($persons1, $persons2)
|
||||
{
|
||||
$same = count($persons1) === count($persons2);
|
||||
|
||||
if (!$same) {
|
||||
return false;
|
||||
}
|
||||
|
||||
array_walk($persons1, function ($name, $key) use ($persons2, &$same) {
|
||||
$family1 = $name->family;
|
||||
$family2 = $persons2[$key]->family;
|
||||
$same = $same && ($family1 === $family2);
|
||||
});
|
||||
|
||||
return (bool) $same;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function normalizeName($data)
|
||||
{
|
||||
if (empty($data->family)) {
|
||||
throw new CiteProcException("Illegal argument. Name has no family name.");
|
||||
}
|
||||
return $data->family.(isset($data->given) ? $data->given : "");
|
||||
}
|
||||
|
||||
public static function addExtendedMarkup($nameVar, $nameItem, $formattedName)
|
||||
{
|
||||
$markupExtension = CiteProc::getContext()->getMarkupExtension();
|
||||
if (array_key_exists($nameVar, $markupExtension)) {
|
||||
$function = $markupExtension[$nameVar];
|
||||
if (is_callable($function)) {
|
||||
return $function($nameItem, $formattedName);
|
||||
}
|
||||
} elseif (array_key_exists($mode = CiteProc::getContext()->getMode(), $markupExtension)) {
|
||||
if (array_key_exists($nameVar, $markupExtension[$mode])) {
|
||||
$function = $markupExtension[$mode][$nameVar];
|
||||
if (is_callable($function)) {
|
||||
return $function($nameItem, $formattedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $formattedName;
|
||||
}
|
||||
}
|
||||
Vendored
+198
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Class Number
|
||||
* @package Seboettg\CiteProc\Util
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class NumberHelper
|
||||
{
|
||||
|
||||
const PATTERN_ORDINAL = "/\d+((st|nd|rd|th)\.?|\.)$/";
|
||||
|
||||
const PATTERN_ROMAN = "/^[ivxlcdm]+\.?$/i";
|
||||
|
||||
const PATTERN_ROMAN_RANGE = "/^([ivxlcdm]+\.*)\s*([*\–\-&+,;])\s*([ivxlcdm]+\.?)$/i";
|
||||
|
||||
const PATTERN_AFFIXES = "/^[a-z]?\d+[a-z]?$/i";
|
||||
|
||||
const PATTERN_COMMA_AMPERSAND_RANGE = "/\d+([\s?\-&+,;\s])+\d+/";
|
||||
|
||||
const ROMAN_NUMERALS = [
|
||||
["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"],
|
||||
["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"],
|
||||
["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"],
|
||||
["", "m", "mm", "mmm", "mmmm", "mmmmm"]
|
||||
];
|
||||
|
||||
const ROMAN_DIGITS = [
|
||||
"M" => 1000,
|
||||
"D" => 500,
|
||||
"C" => 100,
|
||||
"L" => 50,
|
||||
"X" => 10,
|
||||
"V" => 5,
|
||||
"I" => 1
|
||||
];
|
||||
|
||||
/**
|
||||
* @return Closure
|
||||
*/
|
||||
public static function getCompareNumber()
|
||||
{
|
||||
return function ($numA, $numB, $order) {
|
||||
if (is_numeric($numA) && is_numeric($numB)) {
|
||||
$ret = $numA - $numB;
|
||||
} else {
|
||||
$ret = strcasecmp($numA, $numB);
|
||||
}
|
||||
if ("descending" === $order) {
|
||||
return $ret > 0 ? -1 : 1;
|
||||
}
|
||||
return $ret > 0 ? 1 : -1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $num
|
||||
* @return string
|
||||
*/
|
||||
public static function dec2roman($num)
|
||||
{
|
||||
$ret = "";
|
||||
if ($num < 6000) {
|
||||
$numStr = strrev($num);
|
||||
$len = strlen($numStr);
|
||||
for ($pos = 0; $pos < $len; $pos++) {
|
||||
$n = $numStr[$pos];
|
||||
$ret = self::ROMAN_NUMERALS[$pos][$n] . $ret;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $romanNumber
|
||||
* @return int|mixed
|
||||
*/
|
||||
public static function roman2Dec($romanNumber)
|
||||
{
|
||||
$romanNumber = trim($romanNumber);
|
||||
if (is_numeric($romanNumber)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$values = [];
|
||||
// Convert the string to an array of roman values:
|
||||
for ($i = 0; $i < mb_strlen($romanNumber); ++$i) {
|
||||
$char = mb_strtoupper($romanNumber[$i]);
|
||||
if (isset(self::ROMAN_DIGITS[$char])) {
|
||||
$values[] = self::ROMAN_DIGITS[$char];
|
||||
}
|
||||
}
|
||||
|
||||
$sum = 0;
|
||||
while ($current = current($values)) {
|
||||
$next = next($values);
|
||||
$next > $current ? $sum += $next - $current + 0 * next($values) : $sum += $current;
|
||||
}
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRomanNumber($str)
|
||||
{
|
||||
$number = trim($str);
|
||||
for ($i = 0; $i < mb_strlen($number); ++$i) {
|
||||
$char = mb_strtoupper($number[$i]);
|
||||
if (!in_array($char, array_keys(self::ROMAN_DIGITS))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return string
|
||||
*/
|
||||
public static function evaluateStringPluralism($str)
|
||||
{
|
||||
$plural = 'single';
|
||||
if (!empty($str)) {
|
||||
$isRange = self::isRange($str);
|
||||
if ($isRange) {
|
||||
return 'multiple';
|
||||
} else {
|
||||
if (is_numeric($str) || NumberHelper::isRomanNumber($str)) {
|
||||
return 'single';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return mixed
|
||||
*/
|
||||
public static function extractNumber($string)
|
||||
{
|
||||
if (preg_match("/(\d+)[^\d]*$/", $string, $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @return array[]|false|string[]
|
||||
*/
|
||||
public static function splitByRangeDelimiter($str)
|
||||
{
|
||||
return preg_split("/[-–&,]/", $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @return bool
|
||||
*/
|
||||
private static function isRange($str)
|
||||
{
|
||||
$rangeParts = self::splitByRangeDelimiter($str);
|
||||
$isRange = false;
|
||||
if (count($rangeParts) > 1) {
|
||||
$isRange = true;
|
||||
foreach ($rangeParts as $range) {
|
||||
if (NumberHelper::isRomanNumber(trim($range)) || is_numeric(trim($range))) {
|
||||
$isRange = $isRange && true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $isRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $number
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRomanRange($number)
|
||||
{
|
||||
return preg_match(self::PATTERN_ROMAN_RANGE, $number);
|
||||
}
|
||||
}
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2017 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Seboettg\CiteProc\Style\Options\PageRangeFormats;
|
||||
|
||||
/**
|
||||
* Class PageHelper
|
||||
* @package Seboettg\CiteProc\Util
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class PageHelper
|
||||
{
|
||||
/**
|
||||
* @param array $ranges
|
||||
* @param string $pageRangeFormat
|
||||
* @return string
|
||||
*/
|
||||
public static function processPageRangeFormats($ranges, $pageRangeFormat)
|
||||
{
|
||||
list($from, $to) = $ranges;
|
||||
|
||||
if (!empty($pageRangeFormat)) {
|
||||
switch ($pageRangeFormat) {
|
||||
case PageRangeFormats::MINIMAL:
|
||||
$resTo = self::renderMinimal($from, $to, 0);
|
||||
break;
|
||||
case PageRangeFormats::MINIMAL_TWO:
|
||||
if (strlen($to) > 2) {
|
||||
$resTo = self::renderMinimal($from, $to, strlen($to) - 2);
|
||||
} else {
|
||||
$resTo = $to;
|
||||
}
|
||||
break;
|
||||
case PageRangeFormats::CHICAGO:
|
||||
$resTo = self::renderChicago($from, $to);
|
||||
break;
|
||||
case PageRangeFormats::EXPANDED:
|
||||
default:
|
||||
$resTo = $to;
|
||||
}
|
||||
return "$from-$resTo";
|
||||
}
|
||||
return "$from-$to";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $from
|
||||
* @param $to
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
private static function renderMinimal($from, $to, $limit = 1)
|
||||
{
|
||||
$resTo = "";
|
||||
if (strlen($from) == strlen($to)) {
|
||||
for ($i = strlen($to) - 1; $i >= $limit; --$i) {
|
||||
$digitTo = $to[$i];
|
||||
|
||||
$digitFrom = $from[$i];
|
||||
if ($digitTo !== $digitFrom) {
|
||||
$resTo = $digitTo.$resTo;
|
||||
}
|
||||
}
|
||||
return $resTo;
|
||||
}
|
||||
return $to;
|
||||
}
|
||||
|
||||
private static function renderChicago($from, $to)
|
||||
{
|
||||
if ($from > 100 && ($from % 100 > 0) && intval(($from / 100), 10) === intval(($to / 100), 10)) {
|
||||
return "".($to % 100);
|
||||
} elseif ($from >= 10000) {
|
||||
return "".($to % 1000);
|
||||
}
|
||||
return $to;
|
||||
}
|
||||
}
|
||||
Vendored
+317
@@ -0,0 +1,317 @@
|
||||
<?php /** @noinspection PhpInternalEntityUsedInspection */
|
||||
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use Seboettg\CiteProc\CiteProc;
|
||||
use Seboettg\Collection\ArrayList;
|
||||
|
||||
/**
|
||||
* Class StringHelper
|
||||
* @package Seboettg\CiteProc\Util
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class StringHelper
|
||||
{
|
||||
const PREPOSITIONS = [
|
||||
'on', 'in', 'at', 'since', 'for', 'ago', 'before', 'to', 'past', 'till', 'until', 'by', 'under', 'below',
|
||||
'over', 'above', 'across', 'through', 'into', 'towards', 'onto', 'from', 'of', 'off', 'about', 'via'
|
||||
];
|
||||
|
||||
const ARTICLES = [
|
||||
'a', 'an', 'the'
|
||||
];
|
||||
|
||||
const ADVERBS = [
|
||||
'yet', 'so', 'just', 'only'
|
||||
];
|
||||
|
||||
const CONJUNCTIONS = [
|
||||
'nor', 'so', 'and', 'or'
|
||||
];
|
||||
|
||||
const ADJECTIVES = [
|
||||
'down', 'up'
|
||||
];
|
||||
|
||||
const ISO_ENCODINGS = [
|
||||
'ISO-8859-1',
|
||||
'ISO-8859-2',
|
||||
'ISO-8859-3',
|
||||
'ISO-8859-4',
|
||||
'ISO-8859-5',
|
||||
'ISO-8859-6',
|
||||
'ISO-8859-7',
|
||||
'ISO-8859-8',
|
||||
'ISO-8859-9',
|
||||
'ISO-8859-10',
|
||||
'ISO-8859-13',
|
||||
'ISO-8859-14',
|
||||
'ISO-8859-15',
|
||||
'ISO-8859-16'
|
||||
];
|
||||
|
||||
/**
|
||||
* opening quote sign
|
||||
*/
|
||||
const OPENING_QUOTE = "“";
|
||||
|
||||
/**
|
||||
* closing quote sign
|
||||
*/
|
||||
const CLOSING_QUOTE = "”";
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @return string
|
||||
*/
|
||||
public static function capitalizeAll($text)
|
||||
{
|
||||
$wordArray = explode(" ", $text);
|
||||
|
||||
array_walk($wordArray, function (&$word) {
|
||||
$word = ucfirst($word);
|
||||
});
|
||||
|
||||
return implode(" ", array_filter($wordArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $titleString
|
||||
* @return string
|
||||
*/
|
||||
public static function capitalizeForTitle($titleString)
|
||||
{
|
||||
if (strlen($titleString) == 0) {
|
||||
return "";
|
||||
}
|
||||
if (preg_match('/(.+[^\<\>][\.:\/;\?\!]\s?)([a-z])(.+)/', $titleString, $match)) {
|
||||
$titleString = $match[1].StringHelper::mb_ucfirst($match[2]).$match[3];
|
||||
}
|
||||
$pattern = "/(\s|\/)/";
|
||||
if (!preg_match($pattern, $titleString, $matches)) {
|
||||
return StringHelper::mb_ucfirst($titleString);
|
||||
}
|
||||
$delimiter = $matches[1];
|
||||
$wordArray = preg_split($pattern, $titleString); //explode(" ", $titleString);
|
||||
|
||||
$wordList = new ArrayList(...$wordArray);
|
||||
return $wordList
|
||||
->map(function(string $word) {
|
||||
$wordParts = explode("-", $word);
|
||||
if (count($wordParts) > 1) {
|
||||
$casedWordParts = [];
|
||||
foreach ($wordParts as $w) {
|
||||
$casedWordParts[] = StringHelper::keepLowerCase($w) ? $w : StringHelper::mb_ucfirst($w);
|
||||
}
|
||||
$word = implode("-", $casedWordParts);
|
||||
}
|
||||
return StringHelper::keepLowerCase($word) ? $word : StringHelper::mb_ucfirst($word);
|
||||
})
|
||||
->collectToString($delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $word
|
||||
* @return bool
|
||||
*/
|
||||
public static function keepLowerCase($word)
|
||||
{
|
||||
// keep lower case if the first char is not an utf-8 letter
|
||||
return in_array($word, self::PREPOSITIONS) ||
|
||||
in_array($word, self::ARTICLES) ||
|
||||
in_array($word, self::CONJUNCTIONS) ||
|
||||
in_array($word, self::ADJECTIVES) ||
|
||||
(bool) preg_match("/[^\p{L}].+/", $word);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param string $encoding
|
||||
* @return string
|
||||
*/
|
||||
// phpcs:disable
|
||||
public static function mb_ucfirst($string, $encoding = 'UTF-8')
|
||||
{// phpcs:enable
|
||||
$strlen = mb_strlen($string, $encoding);
|
||||
if ($strlen == 0) return '';
|
||||
$firstChar = mb_substr($string, 0, 1, $encoding);
|
||||
$then = mb_substr($string, 1, $strlen - 1, $encoding);
|
||||
|
||||
/** @noinspection PhpInternalEntityUsedInspection */
|
||||
// We can not rely on mb_detect_encoding. See https://www.php.net/manual/en/function.mb-detect-encoding.php.
|
||||
// We need to double-check if the first char is not a multibyte char otherwise mb_strtoupper() process it
|
||||
// incorrectly, and it causes issues later. For example 'こ' transforms to 'Á�'.
|
||||
$original_ord = mb_ord($firstChar, $encoding);
|
||||
$encoding = mb_detect_encoding($firstChar, self::ISO_ENCODINGS, true);
|
||||
$new_ord = mb_ord($firstChar, $encoding);
|
||||
return $original_ord === $new_ord && in_array($encoding, self::ISO_ENCODINGS) ?
|
||||
mb_strtoupper($firstChar, $encoding).$then : $firstChar.$then;
|
||||
}
|
||||
// phpcs:disable
|
||||
public static function mb_strrev($string)
|
||||
{// phpcs:enable
|
||||
$result = '';
|
||||
for ($i = mb_strlen($string); $i >= 0; --$i) {
|
||||
$result .= mb_substr($string, $i, 1);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $delimiter
|
||||
* @param string[] $arrayOfStrings
|
||||
* @return string;
|
||||
*/
|
||||
public static function implodeAndPreventConsecutiveChars($delimiter, $arrayOfStrings)
|
||||
{
|
||||
$delim = trim($delimiter);
|
||||
if (!empty($delim)) {
|
||||
foreach ($arrayOfStrings as $key => $textPart) {
|
||||
$pos = mb_strpos(StringHelper::mb_strrev($textPart), StringHelper::mb_strrev($delim));
|
||||
if ($pos === 0) {
|
||||
$length = mb_strlen($textPart) - mb_strlen($delim);
|
||||
$textPart = mb_substr($textPart, 0, $length);
|
||||
$arrayOfStrings[$key] = $textPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode($delimiter, array_filter($arrayOfStrings));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param $initializeSign
|
||||
* @return string
|
||||
*/
|
||||
public static function initializeBySpaceOrHyphen($string, $initializeSign)
|
||||
{
|
||||
$initializeWithHyphen = CiteProc::getContext()->getGlobalOptions()->isInitializeWithHyphen();
|
||||
$res = "";
|
||||
$exploded = explode("-", $string);
|
||||
$i = 0;
|
||||
foreach ($exploded as $explode) {
|
||||
$spaceExploded = explode(" ", $explode);
|
||||
foreach ($spaceExploded as $givenPart) {
|
||||
$firstLetter = mb_substr($givenPart, 0, 1, "UTF-8");
|
||||
if (StringHelper::isLatinString($firstLetter)) {
|
||||
$res .= ctype_upper($firstLetter) ? $firstLetter.$initializeSign : " ".$givenPart." ";
|
||||
} else {
|
||||
$res .= $firstLetter.$initializeSign;
|
||||
}
|
||||
}
|
||||
if ($i < count($exploded) - 1 && $initializeWithHyphen) {
|
||||
$res = rtrim($res)."-";
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function camelCase2Hyphen($string)
|
||||
{
|
||||
$hyphenated = preg_replace("/([A-Z])/", "-$1", $string);
|
||||
$hyphenated = substr($hyphenated, 0, 1) === "-" ? substr($hyphenated, 1) : $hyphenated;
|
||||
return mb_strtolower($hyphenated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkLowerCaseString($string)
|
||||
{
|
||||
return ($string === mb_strtolower($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkUpperCaseString($string)
|
||||
{
|
||||
return ($string === mb_strtoupper($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return mixed
|
||||
*/
|
||||
public static function clearApostrophes($string)
|
||||
{
|
||||
return preg_replace("/\'/", "’", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* replaces outer quotes of $text by given inner quotes
|
||||
*
|
||||
* @param $text
|
||||
* @param $outerOpenQuote
|
||||
* @param $outerCloseQuote
|
||||
* @param $innerOpenQuote
|
||||
* @param $innerCloseQuote
|
||||
* @return string
|
||||
*/
|
||||
public static function replaceOuterQuotes(
|
||||
$text,
|
||||
$outerOpenQuote,
|
||||
$outerCloseQuote,
|
||||
$innerOpenQuote,
|
||||
$innerCloseQuote
|
||||
) {
|
||||
if (preg_match("/(.*)$outerOpenQuote(.+)$outerCloseQuote(.*)/u", $text, $match)) {
|
||||
return $match[1].$innerOpenQuote.$match[2].$innerCloseQuote.$match[3];
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLatinString($string)
|
||||
{
|
||||
return boolval(preg_match_all("/^[\p{Latin}\p{Common}]+$/u", $string));
|
||||
//return !$noLatin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function isCyrillicString($string)
|
||||
{
|
||||
return boolval(preg_match("/^[\p{Cyrillic}\p{Common}]+$/u", $string));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAsianString($string)
|
||||
{
|
||||
return boolval(preg_match("/^[\p{Han}\s\p{P}]*$/u", $string));
|
||||
}
|
||||
|
||||
/**
|
||||
* removes all kind of brackets from a given string
|
||||
* @param $datePart
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeBrackets($datePart)
|
||||
{
|
||||
return str_replace(["[", "]", "(", ")", "{", "}"], "", $datePart);
|
||||
}
|
||||
}
|
||||
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/*
|
||||
* citeproc-php
|
||||
*
|
||||
* @link http://github.com/seboettg/citeproc-php for the source repository
|
||||
* @copyright Copyright (c) 2016 Sebastian Böttger.
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Seboettg\CiteProc\Util;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Seboettg\CiteProc\Exception\CiteProcException;
|
||||
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
|
||||
use Seboettg\CiteProc\Rendering\Name\Names;
|
||||
use SimpleXMLElement;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class Variables
|
||||
* @package Seboettg\CiteProc\Util
|
||||
*
|
||||
* @author Sebastian Böttger <seboettg@gmail.com>
|
||||
*/
|
||||
class Variables
|
||||
{
|
||||
|
||||
const NAME_VARIABLES = [
|
||||
'author', // author
|
||||
'collection-editor', // editor of the collection holding the item (e.g. the series editor for a book)
|
||||
'composer', // composer (e.g. of a musical score)
|
||||
'container-author', // author of the container holding the item (e.g. the book author for a book chapter)
|
||||
'director', // director (e.g. of a film)
|
||||
'editor', // editor
|
||||
'editorial-director', // managing editor (“Directeur de la Publication” in French)
|
||||
'illustrator', // illustrator (e.g. of a children’s book)
|
||||
'interviewer', // interviewer (e.g. of an interview)
|
||||
'original-author', //
|
||||
'recipient', // recipient (e.g. of a letter)
|
||||
'reviewed-author' // author of the item reviewed by the current item
|
||||
];
|
||||
|
||||
const NUMBER_VARIABLES = [
|
||||
'chapter-number', // chapter number
|
||||
'collection-number', // number identifying the collection holding the item (e.g. the series number for a book)
|
||||
'edition', // (container) edition holding the item (e.g. “3” when citing a chapter in the third
|
||||
// edition of a book)
|
||||
'issue', // (container) issue holding the item (e.g. “5” when citing a journal article from
|
||||
// journal volume 2, issue 5)
|
||||
'number', // number identifying the item (e.g. a report number)
|
||||
'number-of-pages', // total number of pages of the cited item
|
||||
'number-of-volumes', // total number of volumes, usable for citing multi-volume books and such
|
||||
'volume' // (container) volume holding the item (e.g. “2” when citing a chapter from book volume 2)
|
||||
];
|
||||
|
||||
const DATE_VARIABLES = [
|
||||
'accessed', // date the item has been accessed
|
||||
'container',
|
||||
'event-date', // date the related event took place
|
||||
'issued', // date the item was issued/published
|
||||
'original-date', // (issue) date of the original version
|
||||
'submitted' // date the item (e.g. a manuscript) has been submitted for publication
|
||||
];
|
||||
|
||||
const STANDARD_VARIABLE = [
|
||||
'abstract', //abstract of the item (e.g. the abstract of a journal article)
|
||||
'annote', //reader’s notes about the item content
|
||||
'archive', //archive storing the item
|
||||
'archive-location', //storage location within an archive (e.g. a box and folder number)
|
||||
'archive-place', //geographic location of the archive
|
||||
'authority', //issuing or judicial authority (e.g. “USPTO” for a patent, “Fairfax Circuit Court” for
|
||||
//a legal case)
|
||||
'call-number', //call number (to locate the item in a library)
|
||||
'citation-label', //label identifying the item in in-text citations of label styles (e.g. “Ferr78”). May
|
||||
//be assigned by the CSL processor based on item metadata.
|
||||
'citation-number', //index (starting at 1) of the cited reference in the bibliography (generated by the CSL
|
||||
//processor)
|
||||
'collection-title', //title of the collection holding the item (e.g. the series title for a book)
|
||||
'container-title', //title of the container holding the item (e.g. the book title for a book chapter, the
|
||||
//journal title for a journal article)
|
||||
'container-title-short', //short/abbreviated form of “container-title” (also accessible through the “short” form
|
||||
//of the “container-title” variable)
|
||||
'dimensions', //physical (e.g. size) or temporal (e.g. running time) dimensions of the item
|
||||
'DOI', //Digital Object Identifier (e.g. “10.1128/AEM.02591-07”)
|
||||
'event', //name of the related event (e.g. the conference name when citing a conference paper)
|
||||
'event-place', //geographic location of the related event (e.g. “Amsterdam, the Netherlands”)
|
||||
'first-reference-note-number', //number of a preceding note containing the first reference to the item. Assigned
|
||||
// by the CSL processor. The variable holds no value for non-note-based styles, or when
|
||||
// the item hasn’t been cited in any preceding notes.
|
||||
'genre', //class, type or genre of the item (e.g. “adventure” for an adventure movie,
|
||||
//“PhD dissertation” for a PhD thesis),
|
||||
'ISBN', //International Standard Book Number
|
||||
'ISSN', //International Standard Serial Number
|
||||
'jurisdiction', //geographic scope of relevance (e.g. “US” for a US patent)
|
||||
'keyword', //keyword(s) or tag(s) attached to the item
|
||||
'locator', //a cite-specific pinpointer within the item (e.g. a page number within a book, or a
|
||||
//volume in a multi-volume work). Must be accompanied in the input data by a label
|
||||
//indicating the locator type (see the Locators term list), which determines which term
|
||||
//is rendered by cs:label when the “locator” variable is selected.
|
||||
'medium', //medium description (e.g. “CD”, “DVD”, etc.)
|
||||
'note', //(short) inline note giving additional item details (e.g. a concise summary or commentary)
|
||||
'original-publisher', //original publisher, for items that have been republished by a different publisher
|
||||
'original-publisher-place', //geographic location of the original publisher (e.g. “London, UK”)
|
||||
'original-title', //title of the original version (e.g. “Война и мир”, the untranslated Russian title of
|
||||
// “War and Peace”)
|
||||
'page', //range of pages the item (e.g. a journal article) covers in a container (e.g. a journal
|
||||
// issue)
|
||||
'page-first', //first page of the range of pages the item (e.g. a journal article) covers in a
|
||||
//container (e.g. a journal issue)
|
||||
'PMCID', //PubMed Central reference number
|
||||
'PMID', //PubMed reference number
|
||||
'publisher', //publisher
|
||||
'publisher-place', //geographic location of the publisher
|
||||
'references', //resources related to the procedural history of a legal case
|
||||
'reviewed-title', //title of the item reviewed by the current item
|
||||
'scale', //scale of e.g. a map
|
||||
'section', //container section holding the item (e.g. “politics” for a newspaper article)
|
||||
'source', //from whence the item originates (e.g. a library catalog or database)
|
||||
'status', //(publication) status of the item (e.g. “forthcoming”)
|
||||
'title', //primary title of the item
|
||||
'title-short', //short/abbreviated form of “title” (also accessible through the “short” form of the
|
||||
//“title” variable)
|
||||
'URL', //Uniform Resource Locator (e.g. “http://aem.asm.org/cgi/content/full/74/9/2766”)
|
||||
'version', //version of the item (e.g. “2.0.9” for a software program)
|
||||
'year-suffix', //disambiguating year suffix in author-date styles (e.g. “a” in “Doe, 1999a”)
|
||||
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDateVariable($name)
|
||||
{
|
||||
return in_array($name, self::DATE_VARIABLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isNumberVariable($name)
|
||||
{
|
||||
return in_array($name, self::NUMBER_VARIABLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isNameVariable($name)
|
||||
{
|
||||
return in_array($name, self::NAME_VARIABLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $data
|
||||
* @param string $variable
|
||||
* @return string
|
||||
* @throws InvalidStylesheetException
|
||||
* @throws CiteProcException
|
||||
*/
|
||||
public static function nameHash(stdClass $data, $variable)
|
||||
{
|
||||
if (!self::isNameVariable($variable)) {
|
||||
throw new InvalidArgumentException("\"$variable\" is not a valid name variable.");
|
||||
}
|
||||
$parent = null;
|
||||
$names = new Names(
|
||||
new SimpleXMLElement(
|
||||
"<names variable=\"$variable\" delimiter=\"-\">".
|
||||
"<name form=\"long\" sort-separator=\",\" name-as-sort-order=\"all\"/></names>"
|
||||
),
|
||||
$parent
|
||||
);
|
||||
return $names->render($data);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user